Browse Source

Clean up the identity project and make it use services common

davidfowl/common-services
David Fowler 1 year ago
committed by Reuben Bond
parent
commit
acd9a6d04b
7 changed files with 76 additions and 195 deletions
  1. +15
    -31
      src/Services/Identity/Identity.API/GlobalUsings.cs
  2. +0
    -69
      src/Services/Identity/Identity.API/IWebHostExtensions.cs
  3. +30
    -44
      src/Services/Identity/Identity.API/Identity.API.csproj
  4. +12
    -26
      src/Services/Identity/Identity.API/Program.cs
  5. +6
    -6
      src/Services/Identity/Identity.API/appsettings.json
  6. +0
    -17
      src/Services/Identity/Identity.API/web.config
  7. +13
    -2
      src/Services/Services.Common/CommonExtensions.cs

+ 15
- 31
src/Services/Identity/Identity.API/GlobalUsings.cs View File

@ -1,7 +1,11 @@
global using Azure.Core;
global using Azure.Identity;
global using HealthChecks.UI.Client;
global using IdentityModel;
global using System;
global using System.Collections.Generic;
global using System.ComponentModel.DataAnnotations;
global using System.IdentityModel.Tokens.Jwt;
global using System.Linq;
global using System.Security.Claims;
global using System.Text.RegularExpressions;
global using System.Threading.Tasks;
global using Duende.IdentityServer;
global using Duende.IdentityServer.Configuration;
global using Duende.IdentityServer.Events;
@ -10,53 +14,33 @@ global using Duende.IdentityServer.Models;
global using Duende.IdentityServer.Services;
global using Duende.IdentityServer.Stores;
global using Duende.IdentityServer.Validation;
global using IdentityModel;
global using Microsoft.AspNetCore.Authentication;
global using Microsoft.AspNetCore.Authorization;
global using Microsoft.AspNetCore.Builder;
global using Microsoft.AspNetCore.Diagnostics.HealthChecks;
global using Microsoft.AspNetCore.Hosting;
global using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
global using Microsoft.AspNetCore.Http;
global using Microsoft.AspNetCore.Identity;
global using Microsoft.AspNetCore.Mvc.Rendering;
global using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
global using Microsoft.AspNetCore.Mvc;
global using Microsoft.AspNetCore.Mvc.Filters;
global using Microsoft.AspNetCore.Mvc.Rendering;
global using Microsoft.EntityFrameworkCore;
global using Microsoft.EntityFrameworkCore.Infrastructure;
global using Microsoft.EntityFrameworkCore.Metadata;
global using Microsoft.EntityFrameworkCore.Migrations;
global using Microsoft.EntityFrameworkCore;
global using Microsoft.eShopOnContainers.Services.Identity.API;
global using Microsoft.eShopOnContainers.Services.Identity.API.Data;
global using Microsoft.eShopOnContainers.Services.Identity.API.Configuration;
global using Microsoft.eShopOnContainers.Services.Identity.API.Data;
global using Microsoft.eShopOnContainers.Services.Identity.API.Models;
global using Microsoft.eShopOnContainers.Services.Identity.API.Services;
global using Microsoft.Extensions.Configuration;
global using Microsoft.Extensions.DependencyInjection;
global using Microsoft.Extensions.Diagnostics.HealthChecks;
global using Microsoft.Extensions.Hosting;
global using Microsoft.Extensions.Logging;
global using Microsoft.Extensions.Options;
global using Polly;
global using System.Collections.Generic;
global using System.ComponentModel.DataAnnotations;
global using System.Data.SqlClient;
global using System.IdentityModel.Tokens.Jwt;
global using System.Linq;
global using System.Security.Claims;
global using System.Text.RegularExpressions;
global using System.Threading.Tasks;
global using System;
global using Microsoft.AspNetCore.Http;
global using Services.Common;


+ 0
- 69
src/Services/Identity/Identity.API/IWebHostExtensions.cs View File

@ -1,69 +0,0 @@
namespace Microsoft.AspNetCore.Hosting
{
public static class IWebHostExtensions
{
public static bool IsInKubernetes(this IWebHost webHost)
{
var cfg = webHost.Services.GetService<IConfiguration>();
var orchestratorType = cfg.GetValue<string>("OrchestratorType");
return orchestratorType?.ToUpper() == "K8S";
}
public static IWebHost MigrateDbContext<TContext>(this IWebHost webHost, Action<TContext, IServiceProvider> seeder) where TContext : DbContext
{
var underK8s = webHost.IsInKubernetes();
using var scope = webHost.Services.CreateScope();
var services = scope.ServiceProvider;
var logger = services.GetRequiredService<ILogger<TContext>>();
var context = services.GetService<TContext>();
try
{
logger.LogInformation("Migrating database associated with context {DbContextName}", typeof(TContext).Name);
if (underK8s)
{
InvokeSeeder(seeder, context, services);
}
else
{
var retries = 10;
var retry = Policy.Handle<SqlException>()
.WaitAndRetry(
retryCount: retries,
sleepDurationProvider: retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
onRetry: (exception, timeSpan, retry, ctx) =>
{
logger.LogWarning(exception, "[{prefix}] Error seeding database (attempt {retry} of {retries})", nameof(TContext), retry, retries);
});
//if the sql server container is not created on run docker compose this
//migration can't fail for network related exception. The retry options for DbContext only
//apply to transient exceptions
// Note that this is NOT applied when running some orchestrators (let the orchestrator to recreate the failing service)
retry.Execute(() => InvokeSeeder(seeder, context, services));
}
logger.LogInformation("Migrated database associated with context {DbContextName}", typeof(TContext).Name);
}
catch (Exception ex)
{
logger.LogError(ex, "An error occurred while migrating the database used on context {DbContextName}", typeof(TContext).Name);
if (underK8s)
{
throw; // Rethrow under k8s because we rely on k8s to re-run the pod
}
}
return webHost;
}
private static void InvokeSeeder<TContext>(Action<TContext, IServiceProvider> seeder, TContext context, IServiceProvider services)
where TContext : DbContext
{
context.Database.Migrate();
seeder(context, services);
}
}
}

+ 30
- 44
src/Services/Identity/Identity.API/Identity.API.csproj View File

@ -4,68 +4,54 @@
<TargetFramework>net7.0</TargetFramework>
<UserSecretsId>aspnet-eShopOnContainers.Identity-90487118-103c-4ff0-b9da-e5e26f7ab0c5</UserSecretsId>
<DockerComposeProjectPath>..\..\..\..\docker-compose.dcproj</DockerComposeProjectPath>
<GenerateErrorForMissingTargetingPacks>false</GenerateErrorForMissingTargetingPacks>
<IsTransformWebConfigDisabled>true</IsTransformWebConfigDisabled>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AspNetCore.HealthChecks.SqlServer" />
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" />
<PackageReference Include="Duende.IdentityServer.AspNetIdentity" />
<PackageReference Include="Duende.IdentityServer.EntityFramework.Storage" />
<PackageReference Include="Duende.IdentityServer.EntityFramework" />
<PackageReference Include="Duende.IdentityServer.Storage" />
<PackageReference Include="Duende.IdentityServer" />
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" />
<PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" />
<PackageReference Include="Microsoft.ApplicationInsights.Kubernetes" />
<PackageReference Include="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.HealthChecks" />
<PackageReference Include="Microsoft.AspNetCore.HealthChecks" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" />
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" />
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" />
<PackageReference Include="Microsoft.EntityFrameworkCore" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" />
<PackageReference Include="Microsoft.Extensions.Logging.AzureAppServices" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" />
<PackageReference Include="Microsoft.Web.LibraryManager.Build" />
<PackageReference Include="Polly" />
<PackageReference Include="Swashbuckle.AspNetCore" />
<PackageReference Include="Swashbuckle.AspNetCore.Newtonsoft" />
<PackageReference Include="System.Data.SqlClient" />
<PackageReference Include="Azure.Extensions.AspNetCore.Configuration.Secrets" />
<PackageReference Include="Azure.Identity" />
</ItemGroup>
<ItemGroup>
<None Include="Views\Account\AccessDenied.cshtml" />
<None Include="Views\Account\LoggedOut.cshtml" />
<None Include="Views\Account\Login.cshtml" />
<None Include="Views\Account\Logout.cshtml" />
<None Include="Views\Consent\Index.cshtml" />
<None Include="Views\Device\Success.cshtml" />
<None Include="Views\Device\UserCodeCapture.cshtml" />
<None Include="Views\Device\UserCodeConfirmation.cshtml" />
<None Include="Views\Diagnostics\Index.cshtml" />
<None Include="Views\Grants\Index.cshtml" />
<None Include="Views\Home\Index.cshtml" />
<None Include="Views\Shared\Error.cshtml" />
<None Include="Views\Shared\Redirect.cshtml" />
<None Include="Views\Shared\_Layout.cshtml" />
<None Include="Views\Shared\_ScopeListItem.cshtml" />
<None Include="Views\Shared\_ValidationSummary.cshtml" />
<None Include="Views\_ViewImports.cshtml" />
<None Include="Views\_ViewStart.cshtml" />
</ItemGroup>
<ProjectExtensions>
<VisualStudio>
<UserProperties appsettings_1json__JsonSchema="" />
</VisualStudio>
</ProjectExtensions>
<ItemGroup>
<None Include="Views\Account\AccessDenied.cshtml" />
<None Include="Views\Account\LoggedOut.cshtml" />
<None Include="Views\Account\Login.cshtml" />
<None Include="Views\Account\Logout.cshtml" />
<None Include="Views\Consent\Index.cshtml" />
<None Include="Views\Device\Success.cshtml" />
<None Include="Views\Device\UserCodeCapture.cshtml" />
<None Include="Views\Device\UserCodeConfirmation.cshtml" />
<None Include="Views\Diagnostics\Index.cshtml" />
<None Include="Views\Grants\Index.cshtml" />
<None Include="Views\Home\Index.cshtml" />
<None Include="Views\Shared\Error.cshtml" />
<None Include="Views\Shared\Redirect.cshtml" />
<None Include="Views\Shared\_Layout.cshtml" />
<None Include="Views\Shared\_ScopeListItem.cshtml" />
<None Include="Views\Shared\_ValidationSummary.cshtml" />
<None Include="Views\_ViewImports.cshtml" />
<None Include="Views\_ViewStart.cshtml" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Services.Common\Services.Common.csproj" />
</ItemGroup>
<ProjectExtensions>
<VisualStudio>
<UserProperties appsettings_1json__JsonSchema="" />
</VisualStudio>
</ProjectExtensions>
</Project>

+ 12
- 26
src/Services/Identity/Identity.API/Program.cs View File

@ -1,19 +1,12 @@
var builder = WebApplication.CreateBuilder(args);
if (builder.Configuration.GetValue<bool>("UseVault", false))
{
TokenCredential credential = new ClientSecretCredential(
builder.Configuration["Vault:TenantId"],
builder.Configuration["Vault:ClientId"],
builder.Configuration["Vault:ClientSecret"]);
builder.Configuration.AddAzureKeyVault(new Uri($"https://{builder.Configuration["Vault:Name"]}.vault.azure.net/"), credential);
}
builder.AddServiceDefaults();
builder.Services.AddControllersWithViews();
builder.Services.AddControllers();
builder.Services.AddRazorPages();
builder.Services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(builder.Configuration.GetConnectionString("IdentityDb")));
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("IdentityDb")));
builder.Services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
@ -35,23 +28,25 @@ builder.Services.AddIdentityServer(options =>
.AddAspNetIdentity<ApplicationUser>()
.AddDeveloperSigningCredential(); // Not recommended for production - you need to store your key material somewhere secure
builder.Services.AddAuthentication();
builder.Services.AddHealthChecks()
.AddCheck("self", () => HealthCheckResult.Healthy())
.AddSqlServer(builder.Configuration.GetConnectionString("IdentityDb"),
.AddSqlServer(_ =>
builder.Configuration.GetRequiredConnectionString("IdentityDb"),
name: "IdentityDB-check",
tags: new string[] { "IdentityDB" });
builder.Services.AddTransient<IProfileService, ProfileService>();
builder.Services.AddTransient<ILoginService<ApplicationUser>, EFLoginService>();
builder.Services.AddTransient<IRedirectService, RedirectService>();
var app = builder.Build();
var pathBase = builder.Configuration["PATH_BASE"];
if (!string.IsNullOrEmpty(pathBase))
if (!await app.CheckHealthAsync())
{
app.UsePathBase(pathBase);
return;
}
app.UseServiceDefaults();
app.UseStaticFiles();
// This cookie policy fixes login issues with Chrome 80+ using HHTP
@ -61,15 +56,6 @@ app.UseIdentityServer();
app.UseAuthorization();
app.MapDefaultControllerRoute();
app.MapHealthChecks("/hc", new HealthCheckOptions()
{
Predicate = _ => true,
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
});
app.MapHealthChecks("/liveness", new HealthCheckOptions
{
Predicate = r => r.Name.Contains("self")
});
// Apply database migration automatically. Note that this approach is not
// recommended for production scenarios. Consider generating SQL scripts from


+ 6
- 6
src/Services/Identity/Identity.API/appsettings.json View File

@ -1,4 +1,10 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"IsClusterEnv": "False",
"MvcClient": "http://localhost:5100",
"SpaClient": "http://localhost:5104",
@ -7,12 +13,6 @@
"ApplicationInsights": {
"InstrumentationKey": ""
},
"UseVault": false,
"Vault": {
"Name": "eshop",
"ClientId": "your-client-id",
"ClientSecret": "your-client-secret"
},
"TokenLifetimeMinutes": 120,
"PermanentTokenLifetimeDays": 365
}

+ 0
- 17
src/Services/Identity/Identity.API/web.config View File

@ -1,17 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<!--
Configure your application settings in appsettings.json. Learn more at http://go.microsoft.com/fwlink/?LinkId=786380
-->
<system.webServer>
<handlers>
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified" />
</handlers>
<aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false" hostingModel="InProcess">
<environmentVariables>
<environmentVariable name="COMPLUS_ForceENC" value="1" />
<environmentVariable name="ASPNETCORE_ENVIRONMENT" value="Development" />
</environmentVariables>
</aspNetCore>
</system.webServer>
</configuration>

+ 13
- 2
src/Services/Services.Common/CommonExtensions.cs View File

@ -292,7 +292,12 @@ public static class CommonExtensions
// }
// }
var eventBusSection = configuration.GetRequiredSection("EventBus");
var eventBusSection = configuration.GetSection("EventBus");
if (!eventBusSection.Exists())
{
return hcBuilder;
}
return eventBusSection["ProviderName"]?.ToLowerInvariant() switch
{
@ -340,7 +345,13 @@ public static class CommonExtensions
// }
// }
var eventBusSection = configuration.GetRequiredSection("EventBus");
var eventBusSection = configuration.GetSection("EventBus");
if (eventBusSection.Exists())
{
return services;
}
if (string.Equals(eventBusSection["ProviderName"], "ServiceBus", StringComparison.OrdinalIgnoreCase))
{
services.AddSingleton<IServiceBusPersisterConnection>(sp =>


Loading…
Cancel
Save