Merge pull request 'fix/build' (#1) from fix/build into main
Reviewed-on: #1
This commit is contained in:
commit
e10b3df501
20 changed files with 548 additions and 287 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -44,3 +44,4 @@ yavsc-pre
|
|||
|
||||
*.env
|
||||
generated/
|
||||
*.lscache
|
||||
|
|
|
|||
5
.vscode/settings.json
vendored
5
.vscode/settings.json
vendored
|
|
@ -13,5 +13,8 @@
|
|||
"fr"
|
||||
],
|
||||
"cSpell.reportUnknownWords": true,
|
||||
"cSpell.language": "fr,fr-FR,en,en-GB"
|
||||
"cSpell.language": "fr,fr-FR,en,en-GB",
|
||||
"chat.tools.terminal.autoApprove": {
|
||||
"dotnet test": true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,10 +5,10 @@
|
|||
<ItemGroup>
|
||||
<PackageVersion Include="AsciiDocSharp" Version="0.2.0" />
|
||||
<PackageVersion Include="AsciiDocSharp.Converters.Html" Version="0.2.0" />
|
||||
<PackageVersion Include="bootstrap" Version="5.3.7" />
|
||||
<PackageVersion Include="coverlet.collector" Version="6.0.4" />
|
||||
<PackageVersion Include="Google.Apis.Calendar.v3" Version="1.69.0.3746" />
|
||||
<PackageVersion Include="Google.Apis.Compute.v1" Version="1.70.0.3871" />
|
||||
<PackageVersion Include="bootstrap" Version="5.3.8" />
|
||||
<PackageVersion Include="coverlet.collector" Version="10.0.0" />
|
||||
<PackageVersion Include="Google.Apis.Calendar.v3" Version="1.73.0.4073" />
|
||||
<PackageVersion Include="Google.Apis.Compute.v1" Version="1.73.0.4107" />
|
||||
<PackageVersion Include="HigginsSoft.IdentityServer8" Version="8.0.5-preview-net9" />
|
||||
<PackageVersion Include="HigginsSoft.IdentityServer8.AspNetIdentity" Version="8.0.5-preview-net9" />
|
||||
<PackageVersion Include="HigginsSoft.IdentityServer8.EntityFramework" Version="8.0.5-preview-net9" />
|
||||
|
|
@ -17,46 +17,47 @@
|
|||
<PackageVersion Include="HigginsSoft.IdentityServer8.Storage" Version="8.0.5-preview-net9" />
|
||||
<PackageVersion Include="IdentityModel.AspNetCore" Version="4.3.0" />
|
||||
<PackageVersion Include="IdentityModel.OidcClient" Version="6.0.0" />
|
||||
<PackageVersion Include="MailKit" Version="4.8.0" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Antiforgery" Version="2.3.0" />
|
||||
<PackageVersion Include="MailKit" Version="4.16.0" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Antiforgery" Version="2.3.9" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Authentication.Google" Version="9.0.7" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.7" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="9.0.7" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Hosting" Version="2.3.0" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Hosting" Version="2.3.9" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Http.Features" Version="5.0.17" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="9.0.7" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Identity.UI" Version="9.0.7" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Identity.UI" Version="10.0.6" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="9.0.7" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Razor" Version="2.3.0" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Razor.Language" Version="6.0.36" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.SignalR" Version="1.2.0" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.StaticFiles" Version="2.3.0" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.SignalR" Version="1.2.9" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.StaticFiles" Version="2.3.9" />
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.7" />
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.InMemory" Version="9.0.7" />
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.7" />
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.7" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="9.0.7" />
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="10.0.6" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="10.0.6" />
|
||||
<PackageVersion Include="Microsoft.Extensions.CommandLineUtils" Version="1.1.1" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Configuration" Version="9.0.7" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="9.0.7" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.7" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Options" Version="9.0.7" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="9.0.7" />
|
||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
|
||||
<PackageVersion Include="Microsoft.Playwright" Version="1.53.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Configuration" Version="10.0.6" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="10.0.6" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging" Version="10.0.6" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Options" Version="10.0.6" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="10.0.6" />
|
||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.4.0" />
|
||||
<PackageVersion Include="Microsoft.Playwright" Version="1.59.0" />
|
||||
<PackageVersion Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="9.0.0" />
|
||||
<PackageVersion Include="MimeKit" Version="4.8.0" />
|
||||
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageVersion Include="MimeKit" Version="4.16.0" />
|
||||
<PackageVersion Include="Newtonsoft.Json" Version="13.0.4" />
|
||||
<PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" />
|
||||
<PackageVersion Include="PayPalMerchantSDK" Version="2.16.250" />
|
||||
<PackageVersion Include="pazof.rules" Version="1.1.3" />
|
||||
<PackageVersion Include="popper.js" Version="1.16.1" />
|
||||
<PackageVersion Include="RazorEngine.NetCore" Version="3.1.0" />
|
||||
<PackageVersion Include="Selenium.WebDriver" Version="4.34.0" />
|
||||
<PackageVersion Include="Serilog.AspNetCore" Version="9.0.0" />
|
||||
<PackageVersion Include="SixLabors.ImageSharp" Version="3.1.10" />
|
||||
<PackageVersion Include="Swashbuckle.AspNetCore" Version="9.0.3" />
|
||||
<PackageVersion Include="System.Security.Cryptography.Pkcs" Version="9.0.7" />
|
||||
<PackageVersion Include="RazorEngine.NetCore" Version="3.1.1" />
|
||||
<PackageVersion Include="Selenium.WebDriver" Version="4.43.0" />
|
||||
<PackageVersion Include="Serilog.AspNetCore" Version="10.0.0" />
|
||||
<PackageVersion Include="SixLabors.ImageSharp" Version="3.1.12" />
|
||||
<PackageVersion Include="Swashbuckle.AspNetCore" Version="10.1.7" />
|
||||
<PackageVersion Include="System.Security.Cryptography.Pkcs" Version="10.0.6" />
|
||||
<PackageVersion Include="xunit" Version="2.9.3" />
|
||||
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.2" />
|
||||
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.5" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<UserSecretsId>1c73094f-959f-4211-b1a1-6a69b236c283</UserSecretsId>
|
||||
<RootNamespace>Yavsc.Api</RootNamespace>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<UserSecretsId>1c73094f-959f-4211-b1a1-6a69b236c283</UserSecretsId>
|
||||
<RootNamespace>Yavsc.Api</RootNamespace>
|
||||
|
|
|
|||
|
|
@ -1,16 +1,18 @@
|
|||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Reflection;
|
||||
using Google.Apis.Util.Store;
|
||||
using IdentityModel;
|
||||
using IdentityServer8;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using IdentityServer8.EntityFramework.Entities;
|
||||
using IdentityServer8.EntityFramework.Services;
|
||||
using IdentityServer8.EntityFramework.Stores;
|
||||
using IdentityServer8.Stores;
|
||||
using IdentityServer8.EntityFramework;
|
||||
using IdentityServer8.Extensions;
|
||||
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using IdentityServer8.Validation;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.DataProtection;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
|
||||
using Microsoft.AspNetCore.Localization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Razor;
|
||||
|
|
@ -22,28 +24,12 @@ using Microsoft.Net.Http.Headers;
|
|||
using Newtonsoft.Json;
|
||||
using Yavsc.Helpers;
|
||||
using Yavsc.Interface;
|
||||
using Yavsc.Interfaces;
|
||||
using Yavsc.Models;
|
||||
using Yavsc.Server.Helpers;
|
||||
using Yavsc.Services;
|
||||
using Yavsc.Settings;
|
||||
using Yavsc.ViewModels.Auth;
|
||||
using Yavsc.Server.Helpers;
|
||||
using System.Security.Cryptography;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using Microsoft.IdentityModel.Protocols.Configuration;
|
||||
using IdentityModel;
|
||||
using Yavsc.Interfaces;
|
||||
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
|
||||
using Npgsql;
|
||||
using System.Reflection;
|
||||
using IdentityServer8.EntityFramework.DbContexts;
|
||||
using IdentityServer8.EntityFramework.Mappers;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using IdentityServer8.EntityFramework.Stores;
|
||||
using IdentityServer8.EntityFramework.Services;
|
||||
using IdentityServer8.EntityFramework.Interfaces;
|
||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
using IdentityServer8.Validation;
|
||||
using IdentityServer8.EntityFramework.Entities;
|
||||
|
||||
namespace Yavsc.Extensions;
|
||||
|
||||
|
|
@ -99,9 +85,20 @@ public static class HostingExtensions
|
|||
options.ResourcesPath = "Resources";
|
||||
}).AddDataAnnotationsLocalization();
|
||||
|
||||
services.AddTransient<ITrueEmailSender, MailSender>()
|
||||
.AddTransient<Microsoft.AspNetCore.Identity.UI.Services.IEmailSender, MailSender>()
|
||||
.AddTransient<IYavscMessageSender, YavscMessageSender>()
|
||||
bool useTestEmailSender = builder.Configuration.GetValue<bool>("UseTestEmailSender", false);
|
||||
|
||||
if (useTestEmailSender)
|
||||
{
|
||||
services.AddTransient<ITrueEmailSender, TestMailSender>()
|
||||
.AddTransient<Microsoft.AspNetCore.Identity.UI.Services.IEmailSender, TestMailSender>();
|
||||
}
|
||||
else
|
||||
{
|
||||
services.AddTransient<ITrueEmailSender, MailSender>()
|
||||
.AddTransient<Microsoft.AspNetCore.Identity.UI.Services.IEmailSender, MailSender>();
|
||||
}
|
||||
|
||||
services.AddTransient<IYavscMessageSender, YavscMessageSender>()
|
||||
.AddTransient<IBillingService, BillingService>()
|
||||
.AddTransient<IDataStore, FileDataStore>((sp) => new FileDataStore("googledatastore", false))
|
||||
.AddTransient<ICalendarManager, CalendarManager>()
|
||||
|
|
@ -150,13 +147,23 @@ public static class HostingExtensions
|
|||
public static IdentityBuilder AddIdentityDBAndStores(this WebApplicationBuilder builder)
|
||||
{
|
||||
IServiceCollection services = builder.Services;
|
||||
services.AddDbContext<ApplicationDbContext>(options =>
|
||||
{
|
||||
options.UseNpgsql(builder.Configuration.GetConnectionString(Constants.YavscConnectionStringName),
|
||||
options => options.MigrationsAssembly(typeof(Program).Assembly));
|
||||
});
|
||||
bool useInMemory = builder.Configuration.GetValue<bool>("UseInMemoryDatabase", false);
|
||||
|
||||
return services.AddIdentity<ApplicationUser, IdentityRole>(
|
||||
if (useInMemory)
|
||||
{
|
||||
services.AddDbContext<ApplicationDbContext>(options =>
|
||||
options.UseInMemoryDatabase("YavscInMemory"));
|
||||
}
|
||||
else
|
||||
{
|
||||
services.AddDbContext<ApplicationDbContext>(options =>
|
||||
{
|
||||
options.UseNpgsql(builder.Configuration.GetConnectionString(Constants.YavscConnectionStringName),
|
||||
options => options.MigrationsAssembly(typeof(Program).Assembly));
|
||||
});
|
||||
}
|
||||
|
||||
var identityBuilder = services.AddIdentity<ApplicationUser, IdentityRole>(
|
||||
options =>
|
||||
{
|
||||
options.SignIn.RequireConfirmedAccount = builder.Environment.IsEnvironment(
|
||||
|
|
@ -166,6 +173,10 @@ public static class HostingExtensions
|
|||
}
|
||||
)
|
||||
.AddEntityFrameworkStores<ApplicationDbContext>();
|
||||
|
||||
services.AddScoped<IUserClaimsPrincipalFactory<ApplicationUser>, UserClaimsPrincipalFactory<ApplicationUser, IdentityRole>>();
|
||||
|
||||
return identityBuilder;
|
||||
}
|
||||
|
||||
private static void AddYavscPolicies(IServiceCollection services)
|
||||
|
|
@ -259,7 +270,7 @@ public static class HostingExtensions
|
|||
|
||||
});
|
||||
}
|
||||
private static IIdentityServerBuilder AddIdentityServer(WebApplicationBuilder builder)
|
||||
public static IIdentityServerBuilder AddIdentityServer(WebApplicationBuilder builder)
|
||||
{
|
||||
builder.Services.Configure<IdentityOptions>(options =>
|
||||
{
|
||||
|
|
@ -269,6 +280,8 @@ public static class HostingExtensions
|
|||
});
|
||||
var migrationsAssembly = typeof(Program).GetTypeInfo().Assembly.GetName().Name;
|
||||
var connectionString = builder.Configuration.GetConnectionString(Constants.YavscConnectionStringName);
|
||||
bool useInMemory = builder.Configuration.GetValue<bool>("UseInMemoryDatabase", false);
|
||||
string inMemoryDatabaseName = "YavscInMemory";
|
||||
|
||||
var identityServerBuilder = builder.Services.AddIdentityServer(options =>
|
||||
{
|
||||
|
|
@ -288,26 +301,40 @@ public static class HostingExtensions
|
|||
.AddResourceStore<ResourceStore>()
|
||||
.AddConfigurationStore(options =>
|
||||
{
|
||||
options.ConfigureDbContext = b => b.UseNpgsql(connectionString,
|
||||
sql => sql.MigrationsAssembly(migrationsAssembly))
|
||||
.UseSeeding((context, _) =>
|
||||
{
|
||||
foreach (String scope in new string[] { "blog", "admin", "contract", "com"})
|
||||
{
|
||||
var testBlog = context.Set<ApiScope>().FirstOrDefault(b => b.Name == scope);
|
||||
if (testBlog == null)
|
||||
if (useInMemory)
|
||||
{
|
||||
context.Set<ApiScope>().Add(new ApiScope { Name = scope });
|
||||
context.SaveChanges();
|
||||
options.ConfigureDbContext = b => b.UseInMemoryDatabase(inMemoryDatabaseName);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
options.ConfigureDbContext = b => b.UseNpgsql(connectionString,
|
||||
sql => sql.MigrationsAssembly(migrationsAssembly))
|
||||
.UseSeeding((context, _) =>
|
||||
{
|
||||
foreach (String scope in new string[] { "blog", "admin", "contract", "com"})
|
||||
{
|
||||
var testBlog = context.Set<ApiScope>().FirstOrDefault(b => b.Name == scope);
|
||||
if (testBlog == null)
|
||||
{
|
||||
context.Set<ApiScope>().Add(new ApiScope { Name = scope });
|
||||
context.SaveChanges();
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
})
|
||||
.AddOperationalStore(options =>
|
||||
{
|
||||
options.ConfigureDbContext = b => b.UseNpgsql(connectionString,
|
||||
sql => sql.MigrationsAssembly(migrationsAssembly));
|
||||
if (useInMemory)
|
||||
{
|
||||
options.ConfigureDbContext = b => b.UseInMemoryDatabase(inMemoryDatabaseName);
|
||||
}
|
||||
else
|
||||
{
|
||||
options.ConfigureDbContext = b => b.UseNpgsql(connectionString,
|
||||
sql => sql.MigrationsAssembly(migrationsAssembly));
|
||||
}
|
||||
});
|
||||
|
||||
if (builder.Environment.IsDevelopment())
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ namespace Yavsc.Helpers
|
|||
}
|
||||
public static string GetSender(this ApplicationUser user)
|
||||
{
|
||||
return user.UserName+" ["+user.Id+"@"+Config.Authority+"]";
|
||||
return user.UserName+" ["+user.Id+"@"+Config.AuthorityDomain+"]";
|
||||
}
|
||||
public static HairCutQueryEvent CreateEvent(this HairMultiCutQuery query,
|
||||
IStringLocalizer SR, BrusherProfile bpr)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<PackageLicenseExpression>WTFPL</PackageLicenseExpression>
|
||||
<UserSecretsId>76e56fc2-1619-40d8-8393-365258b7a21d</UserSecretsId>
|
||||
|
|
@ -31,6 +31,7 @@
|
|||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" />
|
||||
<PackageReference Include="Google.Apis.Compute.v1" />
|
||||
|
|
|
|||
|
|
@ -9,8 +9,24 @@ namespace Yavsc;
|
|||
|
||||
public static class Config
|
||||
{
|
||||
/// <summary>
|
||||
/// Authority URL for IdentityServer, used for authentication and authorization.
|
||||
/// </summary>
|
||||
public static string Authority { get; set; }
|
||||
|
||||
public static string AuthorityDomain
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Uri.TryCreate(Authority, UriKind.Absolute, out var uri))
|
||||
{
|
||||
return uri.GetLeftPart(UriPartial.Authority);
|
||||
}
|
||||
throw new InvalidOperationException("Invalid Authority URL");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static IConfigurationRoot? GoogleWebClientConfiguration { get; set; }
|
||||
public static GoogleServiceAccount? GServiceAccount { get; set; }
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
|
||||
namespace Yavsc.Helpers
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
|
|
@ -16,6 +15,9 @@ namespace Yavsc.Helpers
|
|||
|
||||
public static class WorkflowHelpers
|
||||
{
|
||||
// Synchronization lock for billing service configuration
|
||||
private static readonly object _billingLock = new object();
|
||||
|
||||
public static async Task<List<PerformerProfileViewModel>>
|
||||
ListPerformersAsync(this ApplicationDbContext context,
|
||||
IBillingService billing,
|
||||
|
|
@ -41,54 +43,79 @@ namespace Yavsc.Helpers
|
|||
public static void RegisterBilling<T>(string code, Func<ApplicationDbContext, long,
|
||||
IDecidableQuery> getter) where T : IBillable
|
||||
{
|
||||
if (BillingService.Billing.ContainsKey(code)
|
||||
|| BillingService.GlobalBillingMap.ContainsKey(code))
|
||||
lock (_billingLock)
|
||||
{
|
||||
throw new InvalidOperationException("Billing setup");
|
||||
string typeName = typeof(T).Name;
|
||||
|
||||
// Only add if not already present (idempotent operation)
|
||||
if (!BillingService.Billing.ContainsKey(code))
|
||||
{
|
||||
BillingService.Billing.Add(code, getter);
|
||||
}
|
||||
else if (!BillingService.GlobalBillingMap.ContainsKey(typeName) ||
|
||||
BillingService.GlobalBillingMap[typeName] != code)
|
||||
{
|
||||
throw new InvalidOperationException($"Billing setup: code '{code}' already registered");
|
||||
}
|
||||
|
||||
if (!BillingService.GlobalBillingMap.ContainsKey(typeName))
|
||||
{
|
||||
BillingService.GlobalBillingMap.Add(typeName, code);
|
||||
}
|
||||
else if (BillingService.GlobalBillingMap[typeName] != code)
|
||||
{
|
||||
throw new InvalidOperationException($"Billing setup: type '{typeName}' already registered with different code");
|
||||
}
|
||||
}
|
||||
BillingService.Billing.Add(code, getter);
|
||||
BillingService.GlobalBillingMap.Add(typeof(T).Name, code);
|
||||
}
|
||||
|
||||
public static void ConfigureBillingService()
|
||||
{
|
||||
foreach (var a in System.AppDomain.CurrentDomain.GetAssemblies())
|
||||
lock (_billingLock)
|
||||
{
|
||||
foreach (var c in a.GetTypes())
|
||||
BillingService.Billing.Clear();
|
||||
BillingService.GlobalBillingMap.Clear();
|
||||
BillingService.UserSettings.Clear();
|
||||
Config.ProfileTypes.Clear();
|
||||
|
||||
foreach (var a in System.AppDomain.CurrentDomain.GetAssemblies())
|
||||
{
|
||||
if (c.IsClass && !c.IsAbstract &&
|
||||
c.GetInterface("ISpecializationSettings") != null)
|
||||
foreach (var c in a.GetTypes())
|
||||
{
|
||||
Config.ProfileTypes.Add(c);
|
||||
if (c.IsClass && !c.IsAbstract &&
|
||||
c.GetInterface("ISpecializationSettings") != null)
|
||||
{
|
||||
Config.ProfileTypes.Add(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var propertyInfo in typeof(ApplicationDbContext).GetProperties())
|
||||
{
|
||||
foreach (var attr in propertyInfo.CustomAttributes)
|
||||
foreach (var propertyInfo in typeof(ApplicationDbContext).GetProperties())
|
||||
{
|
||||
// something like a DbSet?
|
||||
if (typeof(Yavsc.Attributes.ActivitySettingsAttribute).IsAssignableFrom(attr.AttributeType))
|
||||
foreach (var attr in propertyInfo.CustomAttributes)
|
||||
{
|
||||
BillingService.UserSettings.Add(propertyInfo);
|
||||
// something like a DbSet?
|
||||
if (typeof(Yavsc.Attributes.ActivitySettingsAttribute).IsAssignableFrom(attr.AttributeType))
|
||||
{
|
||||
BillingService.UserSettings.Add(propertyInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RegisterBilling<HairCutQuery>(BillingCodes.Brush, new Func<ApplicationDbContext, long, IDecidableQuery>
|
||||
((db, id) =>
|
||||
{
|
||||
var query = db.HairCutQueries.Include(q => q.Prestation).Include(q => q.Regularisation).Single(q => q.Id == id);
|
||||
query.SelectedProfile = db.BrusherProfile.Single(b => b.UserId == query.PerformerId);
|
||||
return query;
|
||||
}));
|
||||
|
||||
RegisterBilling<HairMultiCutQuery>(BillingCodes.MBrush, new Func<ApplicationDbContext, long, IDecidableQuery>
|
||||
((db, id) => db.HairMultiCutQueries.Include(q => q.Regularisation).Single(q => q.Id == id)));
|
||||
|
||||
RegisterBilling<RdvQuery>(BillingCodes.Rdv, new Func<ApplicationDbContext, long, IDecidableQuery>
|
||||
((db, id) => db.RdvQueries.Include(q => q.Regularisation).Single(q => q.Id == id)));
|
||||
}
|
||||
|
||||
RegisterBilling<HairCutQuery>(BillingCodes.Brush, new Func<ApplicationDbContext, long, IDecidableQuery>
|
||||
((db, id) =>
|
||||
{
|
||||
var query = db.HairCutQueries.Include(q => q.Prestation).Include(q => q.Regularisation).Single(q => q.Id == id);
|
||||
query.SelectedProfile = db.BrusherProfile.Single(b => b.UserId == query.PerformerId);
|
||||
return query;
|
||||
}));
|
||||
|
||||
RegisterBilling<HairMultiCutQuery>(BillingCodes.MBrush, new Func<ApplicationDbContext, long, IDecidableQuery>
|
||||
((db, id) => db.HairMultiCutQueries.Include(q => q.Regularisation).Single(q => q.Id == id)));
|
||||
|
||||
RegisterBilling<RdvQuery>(BillingCodes.Rdv, new Func<ApplicationDbContext, long, IDecidableQuery>
|
||||
((db, id) => db.RdvQueries.Include(q => q.Regularisation).Single(q => q.Id == id)));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
30
src/Yavsc.Server/Services/TestMailSender.cs
Normal file
30
src/Yavsc.Server/Services/TestMailSender.cs
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Identity.UI.Services;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Yavsc.Interface;
|
||||
|
||||
namespace Yavsc.Services
|
||||
{
|
||||
public class TestMailSender : ITrueEmailSender, IEmailSender
|
||||
{
|
||||
private readonly ILogger<TestMailSender> logger;
|
||||
|
||||
public TestMailSender(ILoggerFactory loggerFactory)
|
||||
{
|
||||
logger = loggerFactory.CreateLogger<TestMailSender>();
|
||||
}
|
||||
|
||||
public Task SendEmailAsync(string email, string subject, string htmlMessage)
|
||||
{
|
||||
logger.LogInformation("[TestMailSender] SendEmailAsync to {Email} subject={Subject}", email, subject);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task<string> SendEmailAsync(string name, string email, string subject, string htmlMessage)
|
||||
{
|
||||
logger.LogInformation("[TestMailSender] SendEmailAsync to {Email} subject={Subject} name={Name}", email, subject, name);
|
||||
return Task.FromResult($"test-message-{Guid.NewGuid()}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,12 @@ namespace Yavsc.Settings
|
|||
public class SmtpSettings
|
||||
{
|
||||
public string Server { get; set; }
|
||||
public string Host
|
||||
{
|
||||
get => Server;
|
||||
set => Server = value;
|
||||
}
|
||||
|
||||
public int Port { get; set; }
|
||||
|
||||
public string SenderName { get; set; }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<UserSecretsId>53bd70e8-ff81-497a-847f-a15fd8ea7a09</UserSecretsId>
|
||||
<RootNamespace>Yavsc.Server</RootNamespace>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<RootNamespace>Yavsc.cli</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
using isnd.tests;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Net.Security;
|
||||
using isnd.tests;
|
||||
using Xunit.Abstractions;
|
||||
using IdentityModel.Client;
|
||||
using System.Net;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Net.Security;
|
||||
|
||||
namespace yavscTests
|
||||
{
|
||||
|
|
@ -20,64 +19,55 @@ namespace yavscTests
|
|||
|
||||
[Fact]
|
||||
public async Task ObtainServiceToken()
|
||||
{
|
||||
var serverUrl = _serverFixture.Addresses.FirstOrDefault(
|
||||
u => u.StartsWith("https:")
|
||||
);
|
||||
{
|
||||
var serverUrl = _serverFixture.Addresses.FirstOrDefault(u => u.StartsWith("https:"));
|
||||
if (string.IsNullOrEmpty(serverUrl))
|
||||
throw new InvalidOperationException("No HTTPS server address found");
|
||||
|
||||
String authority = _serverFixture.SiteSettings.Authority;
|
||||
HttpClient client = NewHttpClient();
|
||||
var disco = await client.GetDiscoveryDocumentAsync(authority);
|
||||
if (disco.IsError) throw new Exception(disco.Error);
|
||||
HttpClient client = NewHttpClient();
|
||||
var disco = await client.GetDiscoveryDocumentAsync(serverUrl);
|
||||
if (disco.IsError) throw new Exception(disco.Error);
|
||||
|
||||
var response = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest
|
||||
{
|
||||
Address = disco.TokenEndpoint,
|
||||
ClientId = _serverFixture.TestClientId,
|
||||
ClientSecret = _serverFixture.TestClientSecret,
|
||||
Scope = "test",
|
||||
GrantType = "client_credentials"
|
||||
});
|
||||
/*"mvc";
|
||||
options.ClientSecret = "49C1A7E1-0C79-4A89-A3D6-A37998FB86B0";*/
|
||||
if (response.IsError) throw new Exception(response.Error);
|
||||
var response = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest
|
||||
{
|
||||
Address = disco.TokenEndpoint,
|
||||
ClientId = _serverFixture.TestClientId,
|
||||
ClientSecret = _serverFixture.TestClientSecret,
|
||||
Scope = "test",
|
||||
GrantType = "client_credentials"
|
||||
});
|
||||
if (response.IsError) throw new Exception(response.Error);
|
||||
}
|
||||
|
||||
}
|
||||
private static HttpClient NewHttpClient()
|
||||
{
|
||||
return new HttpClient(new BypassSslValidationHandler());
|
||||
}
|
||||
|
||||
private static HttpClient NewHttpClient()
|
||||
{
|
||||
return new HttpClient(new BypassSslValidationHandler());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Fact]
|
||||
public async Task ObtainResourceOwnerPasswordToken()
|
||||
{
|
||||
var serverUrl = _serverFixture.Addresses.FirstOrDefault(
|
||||
u => u.StartsWith("https:")
|
||||
);
|
||||
var serverUrl = _serverFixture.Addresses.FirstOrDefault(u => u.StartsWith("https:"));
|
||||
if (string.IsNullOrEmpty(serverUrl))
|
||||
throw new InvalidOperationException("No HTTPS server address found");
|
||||
|
||||
String authority = _serverFixture.SiteSettings.Authority;
|
||||
var client = NewHttpClient();
|
||||
var disco = await client.GetDiscoveryDocumentAsync(authority);
|
||||
var disco = await client.GetDiscoveryDocumentAsync(serverUrl);
|
||||
if (disco.IsError) throw new Exception(disco.Error);
|
||||
|
||||
var response = await client.RequestPasswordTokenAsync(new PasswordTokenRequest
|
||||
{
|
||||
Address = disco.TokenEndpoint,
|
||||
|
||||
ClientId = _serverFixture.TestClientId,
|
||||
ClientSecret = _serverFixture.TestClientSecret,
|
||||
|
||||
UserName = _serverFixture.TestingUserName,
|
||||
Password = _serverFixture.TestingUserPassword,
|
||||
|
||||
Scope = "test",
|
||||
|
||||
Parameters =
|
||||
{
|
||||
Address = disco.TokenEndpoint,
|
||||
ClientId = _serverFixture.TestClientId,
|
||||
ClientSecret = _serverFixture.TestClientSecret,
|
||||
UserName = _serverFixture.TestingUserName,
|
||||
Password = _serverFixture.TestingUserPassword,
|
||||
Scope = "test",
|
||||
Parameters =
|
||||
{
|
||||
{ "acr_values", "tenant:custom_account_store1 foo bar quux" }
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if (response.IsError) throw new Exception(response.Error);
|
||||
|
||||
|
|
@ -90,22 +80,22 @@ namespace yavscTests
|
|||
|
||||
}
|
||||
|
||||
internal class BypassSslValidationHandler : HttpClientHandler
|
||||
{
|
||||
public BypassSslValidationHandler()
|
||||
internal class BypassSslValidationHandler : HttpClientHandler
|
||||
{
|
||||
// Override validation for this handler only
|
||||
ServerCertificateCustomValidationCallback = ValidateCertificate;
|
||||
}
|
||||
|
||||
private bool ValidateCertificate(
|
||||
HttpRequestMessage request,
|
||||
X509Certificate2 certificate,
|
||||
X509Chain chain,
|
||||
SslPolicyErrors errors)
|
||||
{
|
||||
// Accept all certificates (bypass validation)
|
||||
return true;
|
||||
public BypassSslValidationHandler()
|
||||
{
|
||||
// Override validation for this handler only
|
||||
ServerCertificateCustomValidationCallback = ValidateCertificate;
|
||||
}
|
||||
|
||||
private bool ValidateCertificate(
|
||||
HttpRequestMessage request,
|
||||
X509Certificate2? certificate,
|
||||
X509Chain? chain,
|
||||
SslPolicyErrors errors)
|
||||
{
|
||||
// Accept all certificates (bypass validation)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
48
test/yavscTests/NonRegression/BillingServiceTests.cs
Normal file
48
test/yavscTests/NonRegression/BillingServiceTests.cs
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
using Xunit;
|
||||
using Yavsc;
|
||||
using Yavsc.Abstract.Workflow;
|
||||
using Yavsc.Helpers;
|
||||
using Yavsc.Models;
|
||||
using Yavsc.Models.Billing;
|
||||
using Yavsc.Models.Haircut;
|
||||
using Yavsc.Services;
|
||||
|
||||
namespace yavscTests
|
||||
{
|
||||
[Trait("regression", "II")]
|
||||
public class BillingServiceTests
|
||||
{
|
||||
[Fact]
|
||||
public void ConfigureBillingService_CanBeCalledTwiceWithoutThrowing()
|
||||
{
|
||||
// First initialization should populate the billing registry.
|
||||
WorkflowHelpers.ConfigureBillingService();
|
||||
|
||||
int firstBillingCount = BillingService.Billing.Count;
|
||||
int firstSettingsCount = BillingService.UserSettings.Count;
|
||||
int firstProfileTypesCount = Config.ProfileTypes.Count;
|
||||
|
||||
// Second call should be idempotent and not throw.
|
||||
WorkflowHelpers.ConfigureBillingService();
|
||||
|
||||
Assert.Equal(firstBillingCount, BillingService.Billing.Count);
|
||||
Assert.Equal(firstSettingsCount, BillingService.UserSettings.Count);
|
||||
Assert.Equal(firstProfileTypesCount, Config.ProfileTypes.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RegisterBilling_DuplicateRegistrationThrowsInvalidOperationException()
|
||||
{
|
||||
WorkflowHelpers.ConfigureBillingService();
|
||||
|
||||
var firstRegistrar = new Func<ApplicationDbContext, long, IDecidableQuery>((db, id) =>
|
||||
db.HairCutQueries.Include(q => q.Prestation).Include(q => q.Regularisation).Single(q => q.Id == id));
|
||||
|
||||
const string testCode = "TestBrush";
|
||||
|
||||
Assert.Throws<InvalidOperationException>(() =>
|
||||
WorkflowHelpers.RegisterBilling<HairCutQuery>(testCode, firstRegistrar));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,20 +1,25 @@
|
|||
using IdentityServer8.EntityFramework.Entities;
|
||||
using IdentityServer8.Models;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Hosting.Server;
|
||||
using Microsoft.AspNetCore.Hosting.Server.Features;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Yavsc;
|
||||
using Yavsc.Models;
|
||||
using Yavsc.Extensions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Serilog;
|
||||
using Serilog.Events;
|
||||
using Serilog.Sinks.SystemConsole.Themes;
|
||||
using IdentityServer8.EntityFramework.Entities;
|
||||
using IdentityServer8.Models;
|
||||
using System.Net;
|
||||
using System.Security.Cryptography;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using Yavsc;
|
||||
using Yavsc.Extensions;
|
||||
using Yavsc.Models;
|
||||
using Client = IdentityServer8.EntityFramework.Entities.Client;
|
||||
|
||||
namespace isnd.tests
|
||||
|
|
@ -23,35 +28,92 @@ namespace isnd.tests
|
|||
[CollectionDefinition("Web server collection")]
|
||||
public class WebServerFixture : IDisposable
|
||||
{
|
||||
private static readonly Lazy<X509Certificate2> _selfSignedCertificate = new Lazy<X509Certificate2>(CreateSelfSignedCertificate);
|
||||
private static readonly object _sync = new object();
|
||||
private static WebApplication? _app;
|
||||
private static bool _isInitialized = false;
|
||||
private static int _instanceCount = 0;
|
||||
private static readonly List<string> _sharedAddresses = new List<string>();
|
||||
private static string? _sharedTestClientId;
|
||||
private static string? _sharedTestClientSecret;
|
||||
private static string? _sharedTestingUserName;
|
||||
private static string? _sharedTestingUserPassword;
|
||||
private static string? _sharedTestingUserEmail;
|
||||
private static IServiceProvider? _sharedServices;
|
||||
private static IConfiguration? _sharedConfiguration;
|
||||
private static SiteSettings? _sharedSiteSettings;
|
||||
private static Microsoft.Extensions.Logging.ILogger? _sharedLogger;
|
||||
|
||||
public List<string> Addresses { get; private set; } = new List<string>();
|
||||
public Microsoft.Extensions.Logging.ILogger Logger { get; internal set; }
|
||||
public Microsoft.Extensions.Logging.ILogger? Logger { get; internal set; }
|
||||
|
||||
private SiteSettings siteSettings;
|
||||
private SiteSettings? siteSettings;
|
||||
|
||||
public IConfigurationRoot Configuration { get; private set; }
|
||||
public IConfiguration? Configuration { get; private set; }
|
||||
|
||||
private WebApplication app;
|
||||
public string TestClientId { get; private set; }
|
||||
public string? TestClientId { get; private set; }
|
||||
|
||||
public IServiceProvider Services { get; private set; }
|
||||
public string TestingUserName { get; private set; }
|
||||
public string TestingUserPassword { get; private set; }
|
||||
public IServiceProvider? Services { get; private set; }
|
||||
public string? TestingUserName { get; private set; }
|
||||
public string? TestingUserPassword { get; private set; }
|
||||
|
||||
public string ProtectedTestingApiKey { get; internal set; }
|
||||
public ApplicationUser TestingUser { get; private set; }
|
||||
public string? ProtectedTestingApiKey { get; internal set; }
|
||||
public ApplicationUser? TestingUser { get; private set; }
|
||||
public bool DbCreated { get; internal set; }
|
||||
public SiteSettings SiteSettings { get => siteSettings; set => siteSettings = value; }
|
||||
public string TestClientSecret { get; set; }
|
||||
|
||||
public SiteSettings? SiteSettings { get => siteSettings; set => siteSettings = value; }
|
||||
public string? TestClientSecret { get; set; }
|
||||
public string? TestingUserEmail { get; set; }
|
||||
public WebServerFixture()
|
||||
{
|
||||
SetupHost().Wait();
|
||||
lock (_sync)
|
||||
{
|
||||
_instanceCount++;
|
||||
if (!_isInitialized)
|
||||
{
|
||||
SetupHost().Wait();
|
||||
_isInitialized = true;
|
||||
}
|
||||
|
||||
CopySharedState();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (app != null)
|
||||
app.StopAsync().Wait();
|
||||
lock (_sync)
|
||||
{
|
||||
_instanceCount--;
|
||||
if (_instanceCount == 0 && _app != null)
|
||||
{
|
||||
_app.StopAsync().Wait();
|
||||
_app = null;
|
||||
_isInitialized = false;
|
||||
_sharedAddresses.Clear();
|
||||
_sharedServices = null;
|
||||
_sharedConfiguration = null;
|
||||
_sharedSiteSettings = null;
|
||||
_sharedLogger = null;
|
||||
_sharedTestClientId = null;
|
||||
_sharedTestClientSecret = null;
|
||||
_sharedTestingUserName = null;
|
||||
_sharedTestingUserPassword = null;
|
||||
_sharedTestingUserEmail = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CopySharedState()
|
||||
{
|
||||
Addresses = new List<string>(_sharedAddresses);
|
||||
Logger = _sharedLogger;
|
||||
Configuration = _sharedConfiguration;
|
||||
Services = _sharedServices;
|
||||
SiteSettings = _sharedSiteSettings;
|
||||
TestClientId = _sharedTestClientId;
|
||||
TestClientSecret = _sharedTestClientSecret;
|
||||
TestingUserName = _sharedTestingUserName;
|
||||
TestingUserPassword = _sharedTestingUserPassword;
|
||||
TestingUserEmail = _sharedTestingUserEmail;
|
||||
}
|
||||
void ConfigureLogger() => Log.Logger = new LoggerConfiguration()
|
||||
.MinimumLevel.Debug()
|
||||
|
|
@ -73,18 +135,38 @@ namespace isnd.tests
|
|||
{
|
||||
|
||||
var builder = WebApplication.CreateBuilder();
|
||||
builder.Environment.EnvironmentName = "Development";
|
||||
ConfigureLogger();
|
||||
Configuration = builder.Configuration
|
||||
.AddJsonFile("appsettings.json")
|
||||
.AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json", optional: true)
|
||||
builder.Configuration
|
||||
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: false)
|
||||
.AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json", optional: true, reloadOnChange: false)
|
||||
.AddEnvironmentVariables()
|
||||
.Build();
|
||||
.AddInMemoryCollection(new Dictionary<string, string?>
|
||||
{
|
||||
["UseInMemoryDatabase"] = "true",
|
||||
["UseTestEmailSender"] = "true",
|
||||
["Smtp:Host"] = "localhost",
|
||||
["Smtp:Port"] = "25",
|
||||
["Smtp:SenderName"] = "Yavsc Test",
|
||||
["Smtp:SenderEmail"] = "test@example.com"
|
||||
});
|
||||
|
||||
this.app = builder.ConfigureWebAppServices();
|
||||
Services = app.Services;
|
||||
SiteSettings = app.Services.GetRequiredService<IOptions<SiteSettings>>().Value;
|
||||
|
||||
using (var migrationScope = app.Services.CreateScope())
|
||||
// Configure Kestrel for HTTPS with self-signed certificate on a dynamic port
|
||||
builder.WebHost.ConfigureKestrel(options =>
|
||||
{
|
||||
options.Listen(IPAddress.Loopback, 0, listenOptions =>
|
||||
{
|
||||
listenOptions.UseHttps(_selfSignedCertificate.Value);
|
||||
});
|
||||
});
|
||||
|
||||
Configuration = builder.Configuration;
|
||||
|
||||
_app = builder.ConfigureWebAppServices();
|
||||
Services = _app.Services;
|
||||
SiteSettings = _app.Services.GetRequiredService<IOptions<SiteSettings>>().Value;
|
||||
|
||||
using (var migrationScope = _app.Services.CreateScope())
|
||||
{
|
||||
var db = migrationScope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
|
||||
db.Database.EnsureDeleted();
|
||||
|
|
@ -92,102 +174,110 @@ namespace isnd.tests
|
|||
TestingUserName = "Tester";
|
||||
TestingUserPassword = "tesT456+*";
|
||||
TestClientId = "testClientId";
|
||||
TestingUserEmail = "test@no-reply.com";
|
||||
TestingUser = null;
|
||||
TestClientSecret = Guid.CreateVersion7().ToString();
|
||||
EnsureUser(TestingUserName, TestingUserPassword);
|
||||
AddAuthorizedClient(TestClientId, TestClientSecret);
|
||||
EnsureUser(TestingUserName, TestingUserPassword, TestingUserEmail, migrationScope);
|
||||
AddAuthorizedClient(migrationScope, TestClientId, TestClientSecret);
|
||||
TestingUser = await db.Users.FirstOrDefaultAsync(u => u.UserName == TestingUserName);
|
||||
}
|
||||
await app.ConfigurePipeline();
|
||||
app.UseSession();
|
||||
await app.StartAsync();
|
||||
await _app!.ConfigurePipeline();
|
||||
_app.UseSession();
|
||||
await _app.StartAsync();
|
||||
|
||||
_sharedServices = _app.Services;
|
||||
_sharedConfiguration = Configuration;
|
||||
_sharedSiteSettings = SiteSettings;
|
||||
_sharedTestClientId = TestClientId;
|
||||
_sharedTestClientSecret = TestClientSecret;
|
||||
_sharedTestingUserName = TestingUserName;
|
||||
_sharedTestingUserPassword = TestingUserPassword;
|
||||
_sharedTestingUserEmail = TestingUserEmail;
|
||||
_sharedLogger = _app.Services.GetRequiredService<ILoggerFactory>().CreateLogger<WebServerFixture>();
|
||||
Logger = _sharedLogger;
|
||||
|
||||
|
||||
var logFactory = app.Services.GetRequiredService<ILoggerFactory>();
|
||||
Logger = logFactory.CreateLogger<WebServerFixture>();
|
||||
|
||||
var server = app.Services.GetRequiredService<IServer>();
|
||||
|
||||
var server = _app.Services.GetRequiredService<IServer>();
|
||||
var addressFeatures = server.Features.Get<IServerAddressesFeature>();
|
||||
|
||||
foreach (var address in addressFeatures.Addresses)
|
||||
if (addressFeatures?.Addresses != null)
|
||||
{
|
||||
Addresses.Add(address);
|
||||
_sharedAddresses.Clear();
|
||||
foreach (var address in addressFeatures.Addresses)
|
||||
{
|
||||
_sharedAddresses.Add(address);
|
||||
Addresses.Add(address);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void AddAuthorizedClient(string testClientId, string testClientSecret)
|
||||
private void AddAuthorizedClient(IServiceScope scope, string testClientId, string testClientSecret)
|
||||
{
|
||||
using (IServiceScope scope = app.Services.CreateScope())
|
||||
|
||||
var db = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
|
||||
Client testingClient = new Client
|
||||
{
|
||||
var db = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
|
||||
Client testingClient = new Client
|
||||
{
|
||||
ClientId = testClientId,
|
||||
AccessTokenLifetime = 3600000,
|
||||
AccessTokenType = 1,
|
||||
BackChannelLogoutUri = SiteSettings.Audience,
|
||||
ClientName = "Testing client",
|
||||
Enabled = true
|
||||
};
|
||||
db.Clients.Add(testingClient);
|
||||
db.SaveChanges();
|
||||
ClientSecret secret = new ClientSecret
|
||||
{
|
||||
Value = testClientSecret.Sha256(),
|
||||
ClientId = testingClient.Id
|
||||
};
|
||||
db.ClientSecrets.Add(secret);
|
||||
ClientId = testClientId,
|
||||
AccessTokenLifetime = 3600000,
|
||||
AccessTokenType = 1,
|
||||
BackChannelLogoutUri = SiteSettings!.Audience,
|
||||
ClientName = "Testing client",
|
||||
Enabled = true
|
||||
};
|
||||
db.Clients.Add(testingClient);
|
||||
db.SaveChanges();
|
||||
ClientSecret secret = new ClientSecret
|
||||
{
|
||||
Value = testClientSecret.Sha256(),
|
||||
ClientId = testingClient.Id
|
||||
};
|
||||
db.ClientSecrets.Add(secret);
|
||||
|
||||
var testOrigin = new ClientCorsOrigin
|
||||
{
|
||||
ClientId = testingClient.Id,
|
||||
Origin = SiteSettings.Audience
|
||||
var testOrigin = new ClientCorsOrigin
|
||||
{
|
||||
ClientId = testingClient.Id,
|
||||
Origin = SiteSettings!.Audience
|
||||
|
||||
};
|
||||
db.ClientCorsOrigins.Add(testOrigin);
|
||||
db.ClientGrantTypes.Add(new ClientGrantType
|
||||
{
|
||||
ClientId = testingClient.Id,
|
||||
GrantType = "client_credentials"
|
||||
});
|
||||
db.ClientGrantTypes.Add(new ClientGrantType
|
||||
{
|
||||
ClientId = testingClient.Id,
|
||||
GrantType = "password"
|
||||
});
|
||||
db.ClientGrantTypes.Add(new ClientGrantType
|
||||
{
|
||||
ClientId = testingClient.Id,
|
||||
GrantType = "code"
|
||||
});
|
||||
db.ClientScopes.Add(new ClientScope
|
||||
{
|
||||
ClientId = testingClient.Id,
|
||||
Scope = "test"
|
||||
});
|
||||
db.ApiScopes.Add(new IdentityServer8.EntityFramework.Entities.ApiScope
|
||||
{
|
||||
Name = "test",
|
||||
Enabled = true
|
||||
});
|
||||
db.ClientRedirectUris.Add(new ClientRedirectUri
|
||||
{
|
||||
ClientId = testingClient.Id,
|
||||
RedirectUri = SiteSettings.Audience
|
||||
};
|
||||
db.ClientCorsOrigins.Add(testOrigin);
|
||||
db.ClientGrantTypes.Add(new ClientGrantType
|
||||
{
|
||||
ClientId = testingClient.Id,
|
||||
GrantType = "client_credentials"
|
||||
});
|
||||
db.ClientGrantTypes.Add(new ClientGrantType
|
||||
{
|
||||
ClientId = testingClient.Id,
|
||||
GrantType = "password"
|
||||
});
|
||||
db.ClientGrantTypes.Add(new ClientGrantType
|
||||
{
|
||||
ClientId = testingClient.Id,
|
||||
GrantType = "code"
|
||||
});
|
||||
db.ClientScopes.Add(new ClientScope
|
||||
{
|
||||
ClientId = testingClient.Id,
|
||||
Scope = "test"
|
||||
});
|
||||
db.ApiScopes.Add(new IdentityServer8.EntityFramework.Entities.ApiScope
|
||||
{
|
||||
Name = "test",
|
||||
Enabled = true
|
||||
});
|
||||
db.ClientRedirectUris.Add(new ClientRedirectUri
|
||||
{
|
||||
ClientId = testingClient.Id,
|
||||
RedirectUri = SiteSettings!.Audience
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
db.SaveChanges();
|
||||
}
|
||||
db.SaveChanges();
|
||||
}
|
||||
|
||||
public void EnsureUser(string testingUserName, string password)
|
||||
public void EnsureUser(string testingUserName, string password, string email, IServiceScope scope)
|
||||
{
|
||||
if (TestingUser == null)
|
||||
{
|
||||
using IServiceScope scope = app.Services.CreateScope();
|
||||
|
||||
var userManager =
|
||||
scope.ServiceProvider.GetRequiredService<UserManager<ApplicationUser>>();
|
||||
|
|
@ -208,5 +298,21 @@ namespace isnd.tests
|
|||
TestingUser = dbContext.Users.FirstOrDefault(u => u.UserName == testingUserName);
|
||||
}
|
||||
}
|
||||
|
||||
private static X509Certificate2 CreateSelfSignedCertificate()
|
||||
{
|
||||
var rsa = RSA.Create(2048);
|
||||
var certRequest = new CertificateRequest("CN=localhost", rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
|
||||
|
||||
certRequest.CertificateExtensions.Add(
|
||||
new X509KeyUsageExtension(X509KeyUsageFlags.DataEncipherment | X509KeyUsageFlags.KeyEncipherment | X509KeyUsageFlags.DigitalSignature, false));
|
||||
|
||||
certRequest.CertificateExtensions.Add(
|
||||
new X509EnhancedKeyUsageExtension(
|
||||
new OidCollection { new Oid("1.3.6.1.5.5.7.3.1") }, false));
|
||||
|
||||
var certificate = certRequest.CreateSelfSigned(new DateTimeOffset(DateTime.UtcNow.AddDays(-1)), new DateTimeOffset(DateTime.UtcNow.AddDays(3650)));
|
||||
return certificate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"Site": {
|
||||
"Authority": "localhost",
|
||||
"Authority": "https://localhost",
|
||||
"Title": "Yavsc dev",
|
||||
"Slogan": "Yavsc : WIP.",
|
||||
"Banner": "/images/yavsc.png",
|
||||
|
|
@ -41,7 +41,7 @@
|
|||
}
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
"YavscConnection": "Server=lame-NpgsqlHostName;Port=5432;Database=lame-DataBase;Username=lame-Username;Password=lame-dbPassword;",
|
||||
"YavscConnection": "Server=lame-NpgsqlHostName;Port=5432;Database=lame-DataBase;Username=lame-Username;Password=lame-dbPassword;"
|
||||
},
|
||||
"DataProtection": {
|
||||
"Keys": {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<IsPackable>false</IsPackable>
|
||||
|
|
@ -24,6 +24,11 @@
|
|||
<PackageReference Include="xunit" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="appsettings.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Yavsc.Org\Yavsc.Org.csproj" />
|
||||
<ProjectReference Include="..\..\src\Yavsc.Abstract\Yavsc.Abstract.csproj" />
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue