From a8ae6f7b18508cc9b6e7cdfcbf231e4aef3cacd7 Mon Sep 17 00:00:00 2001 From: Karsten Jeppesen Date: Fri, 28 Aug 2026 08:35:19 +0200 Subject: [PATCH] Should fail --- CommitHookTest/CommitHookTest.slnx | 4 + .../Controllers/WeatherForecastController.cs | 37 +++ CommitHookTest/OpenID-API/Dockerfile | 30 +++ CommitHookTest/OpenID-API/OpenID-API.csproj | 19 ++ CommitHookTest/OpenID-API/OpenID-API.http | 6 + .../OpenIDConnect/OpenIDConnectUtils.cs | 225 ++++++++++++++++++ CommitHookTest/OpenID-API/Program.cs | 25 ++ .../OpenID-API/Properties/launchSettings.json | 35 +++ CommitHookTest/OpenID-API/WeatherForecast.cs | 13 + .../OpenID-API/appsettings.Development.json | 8 + CommitHookTest/OpenID-API/appsettings.json | 16 ++ CommitHookTest/TestProject/MSTestSettings.cs | 1 + CommitHookTest/TestProject/Test1.cs | 13 + CommitHookTest/TestProject/TestProject.csproj | 18 ++ 14 files changed, 450 insertions(+) create mode 100644 CommitHookTest/CommitHookTest.slnx create mode 100644 CommitHookTest/OpenID-API/Controllers/WeatherForecastController.cs create mode 100644 CommitHookTest/OpenID-API/Dockerfile create mode 100644 CommitHookTest/OpenID-API/OpenID-API.csproj create mode 100644 CommitHookTest/OpenID-API/OpenID-API.http create mode 100644 CommitHookTest/OpenID-API/OpenIDConnect/OpenIDConnectUtils.cs create mode 100644 CommitHookTest/OpenID-API/Program.cs create mode 100644 CommitHookTest/OpenID-API/Properties/launchSettings.json create mode 100644 CommitHookTest/OpenID-API/WeatherForecast.cs create mode 100644 CommitHookTest/OpenID-API/appsettings.Development.json create mode 100644 CommitHookTest/OpenID-API/appsettings.json create mode 100644 CommitHookTest/TestProject/MSTestSettings.cs create mode 100644 CommitHookTest/TestProject/Test1.cs create mode 100644 CommitHookTest/TestProject/TestProject.csproj diff --git a/CommitHookTest/CommitHookTest.slnx b/CommitHookTest/CommitHookTest.slnx new file mode 100644 index 0000000..14f1b0c --- /dev/null +++ b/CommitHookTest/CommitHookTest.slnx @@ -0,0 +1,4 @@ + + + + diff --git a/CommitHookTest/OpenID-API/Controllers/WeatherForecastController.cs b/CommitHookTest/OpenID-API/Controllers/WeatherForecastController.cs new file mode 100644 index 0000000..25b234b --- /dev/null +++ b/CommitHookTest/OpenID-API/Controllers/WeatherForecastController.cs @@ -0,0 +1,37 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace OpenID_API.Controllers +{ + [ApiController] + [Route("[controller]")] + public class WeatherForecastController : ControllerBase + { + private static readonly string[] Summaries = + [ + "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" + ]; + + // Any authenticated user can access this endpoint, regardless of their role. + // This is the most basic level of authorization, which only checks if the user is authenticated. + [Authorize] + // The following lines are examples of how to restrict access to specific roles. Uncomment the desired line(s) to apply the restriction. + // This is the way you can specify multiple roles for an endpoint. You can use a comma-separated list to allow access to users with any of the specified roles. + // [Authorize(Roles = "user")] //Only users with the "user" role can access this endpoint + // [Authorize(Roles = "owner")] //Only users with the "owner" role can access this endpoint + // [Authorize(Roles = "admin")] //Only users with the "admin" role can access this endpoint + // [Authorize(Roles = "user,owner")] //Only users with either the "user" or "owner" role can access this endpoint + [HttpGet] + public IEnumerable Get() + { + var xxx = User; + return Enumerable.Range(1, 5).Select(index => new WeatherForecast + { + Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)), + TemperatureC = Random.Shared.Next(-20, 55), + Summary = Summaries[Random.Shared.Next(Summaries.Length)] + }) + .ToArray(); + } + } +} diff --git a/CommitHookTest/OpenID-API/Dockerfile b/CommitHookTest/OpenID-API/Dockerfile new file mode 100644 index 0000000..94e22e7 --- /dev/null +++ b/CommitHookTest/OpenID-API/Dockerfile @@ -0,0 +1,30 @@ +# See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging. + +# This stage is used when running from VS in fast mode (Default for Debug configuration) +FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS base +USER $APP_UID +WORKDIR /app +EXPOSE 8080 +EXPOSE 8081 + + +# This stage is used to build the service project +FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build +ARG BUILD_CONFIGURATION=Release +WORKDIR /src +COPY ["OpenID_API/OpenID_API.csproj", "OpenID_API/"] +RUN dotnet restore "./OpenID_API/OpenID_API.csproj" +COPY . . +WORKDIR "/src/OpenID_API" +RUN dotnet build "./OpenID_API.csproj" -c $BUILD_CONFIGURATION -o /app/build + +# This stage is used to publish the service project to be copied to the final stage +FROM build AS publish +ARG BUILD_CONFIGURATION=Release +RUN dotnet publish "./OpenID_API.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false + +# This stage is used in production or when running from VS in regular mode (Default when not using the Debug configuration) +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "OpenID_API.dll"] \ No newline at end of file diff --git a/CommitHookTest/OpenID-API/OpenID-API.csproj b/CommitHookTest/OpenID-API/OpenID-API.csproj new file mode 100644 index 0000000..8eebf44 --- /dev/null +++ b/CommitHookTest/OpenID-API/OpenID-API.csproj @@ -0,0 +1,19 @@ + + + + net10.0 + enable + enable + 2d959f13-fec1-48ad-a7f1-455a8d4830d3 + Linux + + + + + + + + + + + diff --git a/CommitHookTest/OpenID-API/OpenID-API.http b/CommitHookTest/OpenID-API/OpenID-API.http new file mode 100644 index 0000000..98fea59 --- /dev/null +++ b/CommitHookTest/OpenID-API/OpenID-API.http @@ -0,0 +1,6 @@ +@OpenID_API_HostAddress = http://localhost:5007 + +GET {{OpenID_API_HostAddress}}/weatherforecast/ +Accept: application/json + +### diff --git a/CommitHookTest/OpenID-API/OpenIDConnect/OpenIDConnectUtils.cs b/CommitHookTest/OpenID-API/OpenIDConnect/OpenIDConnectUtils.cs new file mode 100644 index 0000000..3fbc9f1 --- /dev/null +++ b/CommitHookTest/OpenID-API/OpenIDConnect/OpenIDConnectUtils.cs @@ -0,0 +1,225 @@ +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.AspNetCore.HttpOverrides; +using Microsoft.IdentityModel.Tokens; +using Microsoft.OpenApi; +using System.IdentityModel.Tokens.Jwt; +using System.Text; + + +// Required NuGet Packages +// Microsoft.AspNet.WebApi.Core +// Microsoft.AspNetCore.Authentication.JwtBearer +// Microsoft.AspNetCore.Authentication.OpenIdConnect +// Microsoft.IdentityModel.Protocols.OpenIdConnect + + +namespace OpenID_API.OpenIDConnect +{ + public class OpenIDConnectUtils + { + // Configuration priority from highest to lowest + // Highest: Command line arguments + // : Non-prefixed environment variables + // : User secrets from the .NET User Secrets Manager + // : Any appsettings.{ ENVIRONMENT_NAME }.json files + // : The appsettings.json file + // Lowest : Fallback to the host configuration + + /// + /// Setting up OpenIDConnect authentication (Program.cs) + /// + /// WebApplicationBuilder + + public void ConfigureBuilder(WebApplicationBuilder builder) + { + // Add services to the container. + MyConfiguration.Set(builder.Configuration); + + // >>> This adds the authentication service + builder.Services.AddAuthentication(options => + { + options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme; + options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; + }) + .AddCookie() + .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options => + { + options.Authority = builder.Configuration["OpenIDRealmURI"]; + options.Audience = builder.Configuration["OpenIDClient"]; + //options.TokenValidationParameters.RoleClaimType = "roles"; // adjust if your roles claim is named differently + + options.TokenValidationParameters = new TokenValidationParameters + { + RoleClaimType = "roles", // 👈 IMPORTANT + NameClaimType = "name", + ValidateIssuer = true, + ValidateAudience = true + }; + + options.MapInboundClaims = false; // prevent automatic claim type mapping + options.RequireHttpsMetadata = true; // set to false only for development + options.IncludeErrorDetails = true; + + // Map claim types if needed + //options.TokenValidationParameters = new TokenValidationParameters + //{ + // NameClaimType = "name", + // RoleClaimType = "roles" + //}; + + options.Events = new JwtBearerEvents + { + OnMessageReceived = context => + { + // Optional: log the token or handle custom token retrieval + var token = context.Request.Headers["Authorization"].ToString(); + string path = context.Request.Path; + if (!string.IsNullOrEmpty(token)) + { + Console.WriteLine("Access token"); + Console.WriteLine($"URL: {path}"); + Console.WriteLine($"Token: {token}\r\n"); + } + else + { + Console.WriteLine("Access token"); + Console.WriteLine("URL: " + path); + Console.WriteLine("Token: No access token provided\r\n"); + } + return Task.CompletedTask; + }, + OnTokenValidated = context => + { + var loggerFactory = context.HttpContext.RequestServices.GetRequiredService(); + var logger = loggerFactory.CreateLogger("Api.Authorization"); + var claims = context?.Principal?.Claims; + if (claims is null || !claims.Any()) + { + logger.LogWarning("Claims null or empty"); + return Task.CompletedTask; + } + var claimsBuilder = new StringBuilder(); + claimsBuilder.AppendLine("User Claims:"); + foreach (var claim in claims) + { + claimsBuilder.AppendLine($"[{claim.Type}] - [{claim.Value}]"); + } + logger.LogTrace("{Claims}", claimsBuilder.ToString()); + return Task.CompletedTask; + }, + OnAuthenticationFailed = context => + { + // Optional: log authentication failures + Console.WriteLine("Authentication failed: " + context.Exception.Message); + return Task.CompletedTask; + } + }; + + }); + // <<< End of authentication service setup + + // >>> This adds the authorization policies + builder.Services + .AddAuthorizationBuilder() + .AddPolicy("read_access", builder => + { + // claim, list of acceptable values + builder.RequireClaim("myClaim", "MyClaimValueRO1", "MyClaimValueRO2"); + }) + .AddPolicy("write_access", builder => + { + builder.RequireClaim("myClaim", "MyClaimValueRW1", "MyClaimValueRW2"); + }) + .AddPolicy("Admin", policy => policy.RequireClaim("roles", "admin", "warlock")); + // <<< End of authorization policies setup + + ConfigureBuilderOpenAPI(builder); + + builder.Services.Configure(options => + { + options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto; + options.KnownIPNetworks.Clear(); + options.KnownProxies.Clear(); + }); + + } + + private void ConfigureBuilderOpenAPI(WebApplicationBuilder builder) + { + // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle + builder.Services.AddEndpointsApiExplorer(); + + // Add Swagger with Authorization option + // https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/HEAD/docs/configure-and-customize-swaggergen.md#add-security-definitions-and-requirements + builder.Services.AddSwaggerGen(options => + { + options.SwaggerDoc("v1", new OpenApiInfo + { + Title = "My API", + Version = "v1" + }); + options.AddSecurityDefinition("bearer", new OpenApiSecurityScheme + { + Type = SecuritySchemeType.Http, + Scheme = "bearer", + BearerFormat = "JWT", + Description = "JWT Authorization header using the Bearer scheme." + }); + options.AddSecurityRequirement(document => new OpenApiSecurityRequirement + { + [new OpenApiSecuritySchemeReference("bearer", document)] = [] + }); + }); + // Add Swagger with Authorization option + } + + private void ConfigureAppOpenAPI(WebApplication app) + { + app.MapSwagger(); + app.UseSwagger(); + app.UseSwaggerUI(options => + { + options.SwaggerEndpoint("v1/swagger.json", "Your Name Here"); + }); + } + + + + /// + /// + /// WebApplication + public void ConfigureApp(WebApplication app) + { + if (app.Environment.IsDevelopment()) ConfigureAppOpenAPI(app); + } + + public JwtSecurityToken GetJwtPayload(HttpContext myContext) + { + var handler = new JwtSecurityTokenHandler(); + return handler.ReadJwtToken(myContext.GetTokenAsync("access_token").Result); + } + + public string GetJwtClaim(HttpContext myContext, string theClaim) + { + JwtSecurityToken jwtPayload = GetJwtPayload(myContext); + return jwtPayload.Claims.FirstOrDefault(claim => claim.Type == theClaim).Value; + } + + } + + static public class MyConfiguration + { + static ConfigurationManager _config; + static public void Set(ConfigurationManager config) + { + _config = config; + } + + static public ConfigurationManager Get() + { + return _config; + } + + } +} diff --git a/CommitHookTest/OpenID-API/Program.cs b/CommitHookTest/OpenID-API/Program.cs new file mode 100644 index 0000000..0670157 --- /dev/null +++ b/CommitHookTest/OpenID-API/Program.cs @@ -0,0 +1,25 @@ +using OpenID_API.OpenIDConnect; + +var builder = WebApplication.CreateBuilder(args); + +// Configure for OpenID +OpenIDConnectUtils oidcConfig = new(); +oidcConfig.ConfigureBuilder(builder); + +// Add services to the container. + +builder.Services.AddControllers(); + +var app = builder.Build(); + +// Configure for OpenID +oidcConfig.ConfigureApp(app); + +app.UseHttpsRedirection(); + +app.UseAuthentication(); +app.UseAuthorization(); + +app.MapControllers(); + +app.Run(); diff --git a/CommitHookTest/OpenID-API/Properties/launchSettings.json b/CommitHookTest/OpenID-API/Properties/launchSettings.json new file mode 100644 index 0000000..051717e --- /dev/null +++ b/CommitHookTest/OpenID-API/Properties/launchSettings.json @@ -0,0 +1,35 @@ +{ + "profiles": { + "http": { + "commandName": "Project", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "dotnetRunMessages": true, + "applicationUrl": "http://localhost:8887" + }, + "https": { + "commandName": "Project", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "dotnetRunMessages": true, + "applicationUrl": "https://localhost:8888" + }, + "Container (Dockerfile)": { + "commandName": "Docker", + "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}", + "environmentVariables": { + "ASPNETCORE_HTTPS_PORTS": "8081", + "ASPNETCORE_HTTP_PORTS": "8080" + }, + "publishAllPorts": true, + "useSSL": true + } + }, + "$schema": "https://json.schemastore.org/launchsettings.json" +} \ No newline at end of file diff --git a/CommitHookTest/OpenID-API/WeatherForecast.cs b/CommitHookTest/OpenID-API/WeatherForecast.cs new file mode 100644 index 0000000..d9dbbf5 --- /dev/null +++ b/CommitHookTest/OpenID-API/WeatherForecast.cs @@ -0,0 +1,13 @@ +namespace OpenID_API +{ + public class WeatherForecast + { + public DateOnly Date { get; set; } + + public int TemperatureC { get; set; } + + public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); + + public string? Summary { get; set; } + } +} diff --git a/CommitHookTest/OpenID-API/appsettings.Development.json b/CommitHookTest/OpenID-API/appsettings.Development.json new file mode 100644 index 0000000..0c208ae --- /dev/null +++ b/CommitHookTest/OpenID-API/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/CommitHookTest/OpenID-API/appsettings.json b/CommitHookTest/OpenID-API/appsettings.json new file mode 100644 index 0000000..2f29a4c --- /dev/null +++ b/CommitHookTest/OpenID-API/appsettings.json @@ -0,0 +1,16 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning", + "Microsoft.AspNetCore.Authentication": "Information", + "Microsoft.AspNetCore.Authorization": "Information", + "Api.Authorization": "Trace" + } + }, + "AllowedHosts": "*", + "Comment1": "Configuration for OpenID Connect authentication", + "OpenIDRealmURI": "https://auth.a.ucnit.eu/realms/xOIDCx", + "OpenIDClient": "Alice", + "OpenIDSecret": "JvDnso8O773pE9ENJdRhsrJd5pVD5Q86" +} diff --git a/CommitHookTest/TestProject/MSTestSettings.cs b/CommitHookTest/TestProject/MSTestSettings.cs new file mode 100644 index 0000000..aaf278c --- /dev/null +++ b/CommitHookTest/TestProject/MSTestSettings.cs @@ -0,0 +1 @@ +[assembly: Parallelize(Scope = ExecutionScope.MethodLevel)] diff --git a/CommitHookTest/TestProject/Test1.cs b/CommitHookTest/TestProject/Test1.cs new file mode 100644 index 0000000..20e81f1 --- /dev/null +++ b/CommitHookTest/TestProject/Test1.cs @@ -0,0 +1,13 @@ +namespace TestProject +{ + [TestClass] + public sealed class Test1 + { + [TestMethod] + public void TestMethod1() + { + Assert.Fail("intentionally failing..."); + } + + } +} diff --git a/CommitHookTest/TestProject/TestProject.csproj b/CommitHookTest/TestProject/TestProject.csproj new file mode 100644 index 0000000..60cb8d0 --- /dev/null +++ b/CommitHookTest/TestProject/TestProject.csproj @@ -0,0 +1,18 @@ + + + + net10.0 + latest + enable + enable + + + + + + + + + + +