Should fail
This commit is contained in:
4
CommitHookTest/CommitHookTest.slnx
Normal file
4
CommitHookTest/CommitHookTest.slnx
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<Solution>
|
||||||
|
<Project Path="OpenID-API/OpenID-API.csproj" />
|
||||||
|
<Project Path="TestProject/TestProject.csproj" />
|
||||||
|
</Solution>
|
||||||
@@ -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<WeatherForecast> 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
30
CommitHookTest/OpenID-API/Dockerfile
Normal file
30
CommitHookTest/OpenID-API/Dockerfile
Normal file
@@ -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"]
|
||||||
19
CommitHookTest/OpenID-API/OpenID-API.csproj
Normal file
19
CommitHookTest/OpenID-API/OpenID-API.csproj
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<UserSecretsId>2d959f13-fec1-48ad-a7f1-455a8d4830d3</UserSecretsId>
|
||||||
|
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.11" />
|
||||||
|
<PackageReference Include="Microsoft.IdentityModel.Protocols.OpenIdConnect" Version="8.22.0" />
|
||||||
|
<PackageReference Include="Microsoft.OpenApi" Version="2.12.2" />
|
||||||
|
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.23.0" />
|
||||||
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="10.2.3" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
6
CommitHookTest/OpenID-API/OpenID-API.http
Normal file
6
CommitHookTest/OpenID-API/OpenID-API.http
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
@OpenID_API_HostAddress = http://localhost:5007
|
||||||
|
|
||||||
|
GET {{OpenID_API_HostAddress}}/weatherforecast/
|
||||||
|
Accept: application/json
|
||||||
|
|
||||||
|
###
|
||||||
225
CommitHookTest/OpenID-API/OpenIDConnect/OpenIDConnectUtils.cs
Normal file
225
CommitHookTest/OpenID-API/OpenIDConnect/OpenIDConnectUtils.cs
Normal file
@@ -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
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Setting up OpenIDConnect authentication (Program.cs)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="builder">WebApplicationBuilder</param>
|
||||||
|
|
||||||
|
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<ILoggerFactory>();
|
||||||
|
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<ForwardedHeadersOptions>(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");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="app">WebApplication</param>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
25
CommitHookTest/OpenID-API/Program.cs
Normal file
25
CommitHookTest/OpenID-API/Program.cs
Normal file
@@ -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();
|
||||||
35
CommitHookTest/OpenID-API/Properties/launchSettings.json
Normal file
35
CommitHookTest/OpenID-API/Properties/launchSettings.json
Normal file
@@ -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"
|
||||||
|
}
|
||||||
13
CommitHookTest/OpenID-API/WeatherForecast.cs
Normal file
13
CommitHookTest/OpenID-API/WeatherForecast.cs
Normal file
@@ -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; }
|
||||||
|
}
|
||||||
|
}
|
||||||
8
CommitHookTest/OpenID-API/appsettings.Development.json
Normal file
8
CommitHookTest/OpenID-API/appsettings.Development.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft.AspNetCore": "Warning"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
16
CommitHookTest/OpenID-API/appsettings.json
Normal file
16
CommitHookTest/OpenID-API/appsettings.json
Normal file
@@ -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"
|
||||||
|
}
|
||||||
1
CommitHookTest/TestProject/MSTestSettings.cs
Normal file
1
CommitHookTest/TestProject/MSTestSettings.cs
Normal file
@@ -0,0 +1 @@
|
|||||||
|
[assembly: Parallelize(Scope = ExecutionScope.MethodLevel)]
|
||||||
13
CommitHookTest/TestProject/Test1.cs
Normal file
13
CommitHookTest/TestProject/Test1.cs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
namespace TestProject
|
||||||
|
{
|
||||||
|
[TestClass]
|
||||||
|
public sealed class Test1
|
||||||
|
{
|
||||||
|
[TestMethod]
|
||||||
|
public void TestMethod1()
|
||||||
|
{
|
||||||
|
Assert.Fail("intentionally failing...");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
18
CommitHookTest/TestProject/TestProject.csproj
Normal file
18
CommitHookTest/TestProject/TestProject.csproj
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
|
<LangVersion>latest</LangVersion>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="MSTest" Version="4.0.2" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Using Include="Microsoft.VisualStudio.TestTools.UnitTesting" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
Reference in New Issue
Block a user