From 6c4c96f7fb6564c7816734a94542fd5c889e0437 Mon Sep 17 00:00:00 2001 From: dave Date: Thu, 28 Mar 2024 04:29:54 +0100 Subject: [PATCH] seems to work not tested for the moment --- src/DbContextLib/HeartTrackContext.cs | 23 +++-- src/Dto/Auth/AuthResponseDto.cs | 23 +++++ src/Dto/Auth/RegisterRequestDto.cs | 33 +++++++ .../Controllers/ActivityController.cs | 3 + .../Controllers/AnalysisController.cs | 2 + .../Controllers/UsersController.cs | 1 + src/HeartTrackAPI/Services/TokenService.cs | 48 ++++++++++ src/HeartTrackAPI/Utils/AppBootstrap.cs | 93 ++++++++++--------- src/HeartTrackAPI/appsettings.json | 4 +- 9 files changed, 176 insertions(+), 54 deletions(-) create mode 100644 src/Dto/Auth/AuthResponseDto.cs create mode 100644 src/Dto/Auth/RegisterRequestDto.cs create mode 100644 src/HeartTrackAPI/Services/TokenService.cs diff --git a/src/DbContextLib/HeartTrackContext.cs b/src/DbContextLib/HeartTrackContext.cs index a9c6ad7..8001980 100644 --- a/src/DbContextLib/HeartTrackContext.cs +++ b/src/DbContextLib/HeartTrackContext.cs @@ -236,12 +236,23 @@ namespace DbContextLib .WithOne(at => at.DataSource) .HasForeignKey(at => at.DataSourceId) .IsRequired(false); - List roles = new List - { - new ("Athlete"), - new ("Coach") - }; - modelBuilder.Entity().HasData(roles); + List> roles = + [ + new() + { + Id = 1, + Name = "Athlete", + NormalizedName = "ATHLETE" + }, + + new() + { + Id = 2, + Name = "Coach", + NormalizedName = "COACH" + } + ]; + modelBuilder.Entity>().HasData(roles); } } } \ No newline at end of file diff --git a/src/Dto/Auth/AuthResponseDto.cs b/src/Dto/Auth/AuthResponseDto.cs new file mode 100644 index 0000000..7cab2a4 --- /dev/null +++ b/src/Dto/Auth/AuthResponseDto.cs @@ -0,0 +1,23 @@ +namespace Dto.Auth; + +public class AuthResponseDto +{ + /// + /// Gets or sets the access token issued by the OAuth provider. + /// + public string AccessToken { get; set; } + + /// Gets or sets the token type. + /// Typically the string “bearer”. + public string? TokenType { get; set; } + + /// + /// Gets or sets a refresh token that applications can use to obtain another access token if tokens can expire. + /// + public string? RefreshToken { get; set; } + + /// + /// Gets or sets the validatity lifetime of the token in seconds. + /// + public string? ExpiresIn { get; set; } +} \ No newline at end of file diff --git a/src/Dto/Auth/RegisterRequestDto.cs b/src/Dto/Auth/RegisterRequestDto.cs new file mode 100644 index 0000000..ccd837e --- /dev/null +++ b/src/Dto/Auth/RegisterRequestDto.cs @@ -0,0 +1,33 @@ +using System.ComponentModel.DataAnnotations; + +namespace Dto.Auth; + +public class RegisterRequestDto +{ + + [MaxLength(100)] + [Required(ErrorMessage = "Username is required")] + public string Username { get; set; } + [MaxLength(150)] + [Required(ErrorMessage = "LastName is required")] + public string LastName { get; set; } + [MaxLength(100)] + [Required(ErrorMessage = "FirstName is required")] + public string FirstName { get; set; } + [Required(ErrorMessage = "Email is required")] + [EmailAddress] + public string Email { get; set; } + [Required(ErrorMessage = "Sexe is required")] + public string Sexe { get; set; } + [Required(ErrorMessage = "Size is required")] + public float Size { get; set; } + [Required(ErrorMessage = "Weight is required")] + public float Weight { get; set; } + [Required(ErrorMessage = "Password is required")] + public string Password { get; set; } + [Required(ErrorMessage = "DateOfBirth is required")] + public DateTime DateOfBirth { get; set; } + public string ProfilePicture { get; set; } = "https://davidalmeida.site/assets/me_avatar.f77af006.png"; + [Required(ErrorMessage = "Role is required")] + public bool IsCoach { get; set; } +} \ No newline at end of file diff --git a/src/HeartTrackAPI/Controllers/ActivityController.cs b/src/HeartTrackAPI/Controllers/ActivityController.cs index 40d5bc4..f117dfb 100644 --- a/src/HeartTrackAPI/Controllers/ActivityController.cs +++ b/src/HeartTrackAPI/Controllers/ActivityController.cs @@ -3,6 +3,7 @@ using Dto; using Dto.Tiny; using HeartTrackAPI.Request; using HeartTrackAPI.Responce; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Shared; using Model.Manager; @@ -12,6 +13,8 @@ namespace HeartTrackAPI.Controllers; [ApiController] [ApiVersion("1.0")] [Route("api/v{version:apiVersion}/[controller]")] +[Authorize] + public class ActivityController : Controller { private readonly IActivityRepository _activityService; diff --git a/src/HeartTrackAPI/Controllers/AnalysisController.cs b/src/HeartTrackAPI/Controllers/AnalysisController.cs index 9a7d433..11c3be3 100644 --- a/src/HeartTrackAPI/Controllers/AnalysisController.cs +++ b/src/HeartTrackAPI/Controllers/AnalysisController.cs @@ -1,4 +1,5 @@ using Dto.Tiny; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; namespace HeartTrackAPI.Controllers; @@ -6,6 +7,7 @@ namespace HeartTrackAPI.Controllers; [ApiController] [ApiVersion("1.0")] [Route("api/[controller]")] +[Authorize] public class AnalysisController : Controller { private readonly List _heartRateZones = new() diff --git a/src/HeartTrackAPI/Controllers/UsersController.cs b/src/HeartTrackAPI/Controllers/UsersController.cs index 0463765..6f74657 100644 --- a/src/HeartTrackAPI/Controllers/UsersController.cs +++ b/src/HeartTrackAPI/Controllers/UsersController.cs @@ -20,6 +20,7 @@ namespace HeartTrackAPI.Controllers; [ApiController] [ApiVersion("1.0")] [Route("api/v{version:apiVersion}/[controller]")] +[Authorize] public class UsersController : Controller { private readonly ILogger _logger; diff --git a/src/HeartTrackAPI/Services/TokenService.cs b/src/HeartTrackAPI/Services/TokenService.cs new file mode 100644 index 0000000..c2ce989 --- /dev/null +++ b/src/HeartTrackAPI/Services/TokenService.cs @@ -0,0 +1,48 @@ +using System.IdentityModel.Tokens.Jwt; +using System.Security.Claims; +using System.Text; +using Entities; +using Microsoft.IdentityModel.Tokens; + +namespace HeartTrackAPI.Services; +public interface ITokenService +{ + string CreateToken(AthleteEntity user); +} + +public class TokenService : ITokenService +{ + private readonly IConfiguration _config; + private readonly SymmetricSecurityKey _key; + + public TokenService(IConfiguration config) + { + _config = config; + _key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["JWT:SigningKey"])); + } + public string CreateToken(AthleteEntity user) + { + var claims = new List + { + new (JwtRegisteredClaimNames.Email, user.Email), + new (JwtRegisteredClaimNames.GivenName, user.UserName) + }; + + var creds = new SigningCredentials(_key, SecurityAlgorithms.HmacSha512Signature); + + var tokenDescriptor = new SecurityTokenDescriptor + { + Subject = new ClaimsIdentity(claims), + Expires = DateTime.Now.AddDays(7), + SigningCredentials = creds, + Issuer = _config["JWT:Issuer"], + Audience = _config["JWT:Audience"] + }; + + var tokenHandler = new JwtSecurityTokenHandler(); + + var token = tokenHandler.CreateToken(tokenDescriptor); + + return tokenHandler.WriteToken(token); + } +} \ No newline at end of file diff --git a/src/HeartTrackAPI/Utils/AppBootstrap.cs b/src/HeartTrackAPI/Utils/AppBootstrap.cs index 22c75a5..6ad72db 100644 --- a/src/HeartTrackAPI/Utils/AppBootstrap.cs +++ b/src/HeartTrackAPI/Utils/AppBootstrap.cs @@ -1,6 +1,7 @@ using System.Reflection; using DbContextLib; using Entities; +using HeartTrackAPI.Services; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc.Versioning; @@ -15,28 +16,27 @@ using StubbedContextLib; using Swashbuckle.AspNetCore.SwaggerGen; namespace HeartTrackAPI.Utils; + public class AppBootstrap(IConfiguration configuration) { private IConfiguration Configuration { get; } = configuration; public void ConfigureServices(IServiceCollection services) { - services.AddControllers(); - + services.AddEndpointsApiExplorer(); AddSwagger(services); AddHeartTrackContextServices(services); AddModelService(services); - AddIdentityServices(services,configuration); + AddIdentityServices(services, configuration); AddApiVersioning(services); services.AddHealthChecks(); - } private void AddHeartTrackContextServices(IServiceCollection services) { - string? connectionString; + string? connectionString; switch (Environment.GetEnvironmentVariable("TYPE")) { @@ -52,8 +52,8 @@ public class AppBootstrap(IConfiguration configuration) Console.WriteLine(connectionString); Console.WriteLine("===================================================="); services.AddDbContext(options => - options.UseMySql($"{connectionString}", new MySqlServerVersion(new Version(10, 11, 1))) - , ServiceLifetime.Singleton); + options.UseMySql($"{connectionString}", new MySqlServerVersion(new Version(10, 11, 1))) + , ServiceLifetime.Singleton); break; default: Console.WriteLine("====== RUNNING USING THE IN SQLITE DATABASE ======"); @@ -68,17 +68,18 @@ public class AppBootstrap(IConfiguration configuration) { services.AddDbContext(); } + break; - } } - + private void AddModelService(IServiceCollection services) { switch (Environment.GetEnvironmentVariable("TYPE")) { case "BDD": - services.AddSingleton(provider => new DbDataManager(provider.GetRequiredService())); + services.AddSingleton(provider => + new DbDataManager(provider.GetRequiredService())); break; case "STUB-MODEL": services.AddSingleton(); @@ -93,26 +94,28 @@ public class AppBootstrap(IConfiguration configuration) // services.AddSingleton(provider => new DbDataManager(provider.GetRequiredService())); break; } + // Auth + + services.AddScoped(); } private void AddIdentityServices(IServiceCollection services, IConfiguration config) { - services.AddAuthorization(); + /*services.AddAuthorization(); services.AddIdentityApiEndpoints() - .AddEntityFrameworkStores(); + .AddEntityFrameworkStores();*/ - /*services.AddIdentity>(options => + services.AddIdentity>(options => { - options.PasswordHash.RequireDigit = true; - options.PasswordHash.RequireLowercase = true; - options.PasswordHash.RequireUppercase = true; - options.PasswordHash.RequireNonAlphanumeric = true; - options.PasswordHash.RequiredLength = 8; - + options.Password.RequireDigit = true; + options.Password.RequireLowercase = true; + options.Password.RequireUppercase = true; + options.Password.RequireNonAlphanumeric = true; + options.Password.RequiredLength = 8; }) - .AddEntityFrameworkStores(); - - + .AddEntityFrameworkStores(); + + services.AddAuthentication(options => { options.DefaultAuthenticateScheme = @@ -126,16 +129,16 @@ public class AppBootstrap(IConfiguration configuration) options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, - //ValidIssuer =config["JWT:Issuer"], + ValidIssuer =config["JWT:Issuer"], ValidateAudience = true, - //ValidAudience = config["JWT:Audience"], + ValidAudience = config["JWT:Audience"], ValidateIssuerSigningKey = true, IssuerSigningKey = new SymmetricSecurityKey( System.Text.Encoding.UTF8.GetBytes(config["JWT:SigningKey"]) ) }; - });*/ - + }); + /* app.UseCors(x => x .AllowAnyMethod() @@ -151,10 +154,9 @@ public class AppBootstrap(IConfiguration configuration) // .AddEntityFrameworkStores(); // .AddEntityFrameworkStores().AddDefaultTokenProviders(); } - + private void AddApiVersioning(IServiceCollection services) { - services.AddApiVersioning(opt => { opt.ReportApiVersions = true; @@ -164,8 +166,8 @@ public class AppBootstrap(IConfiguration configuration) new HeaderApiVersionReader("x-api-version"), new MediaTypeApiVersionReader("x-api-version")); }); - } + private void AddSwagger(IServiceCollection services) { services.AddSwaggerGen(options => @@ -173,14 +175,16 @@ public class AppBootstrap(IConfiguration configuration) var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"; var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile); options.IncludeXmlComments(xmlPath); - + options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme { Description = "JWT Authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\"", Name = "Authorization", In = ParameterLocation.Header, - Type = SecuritySchemeType.ApiKey + Type = SecuritySchemeType.Http, + Scheme = "Bearer", + BearerFormat = "JWT" }); var scheme = new OpenApiSecurityRequirement { @@ -199,33 +203,32 @@ public class AppBootstrap(IConfiguration configuration) new List() } }; - + options.AddSecurityRequirement(scheme); options.OperationFilter(); - }); - //services.AddTransient, SwaggerOptions>(); - /* + services.AddTransient, SwaggerOptions>(); + services.AddVersionedApiExplorer(setup => { setup.GroupNameFormat = "'v'VVV"; setup.SubstituteApiVersionInUrl = true; - });*/ - + }); } public void Configure(WebApplication app, IWebHostEnvironment env) { app.UseHttpsRedirection(); - + app.UseAuthentication(); app.UseAuthorization(); - app.MapIdentityApi(); + +// app.MapIdentityApi(); app.MapControllers(); app.MapHealthChecks("/health"); - + // Configure the HTTP request pipeline. if (true) { @@ -233,13 +236,12 @@ public class AppBootstrap(IConfiguration configuration) { options.PreSerializeFilters.Add((swagger, httpReq) => { - if (httpReq.Headers.ContainsKey("X-Forwarded-Host")) { string basePath; switch (Environment.GetEnvironmentVariable("TYPE")) // httpReq.Host.Value { - case "STUB" : + case "STUB": basePath = "containers/HeartDev-heart_stub"; break; case "BDD": @@ -249,15 +251,14 @@ public class AppBootstrap(IConfiguration configuration) basePath = httpReq.Host.Value; break; } + var serverUrl = $"https://{httpReq.Headers["X-Forwarded-Host"]}/{basePath}"; - swagger.Servers = new List { new() { Url = serverUrl } }; + swagger.Servers = new List { new() { Url = serverUrl } }; } }); }); app.UseSwaggerUI(); app.MapSwagger(); } - - } -} +} \ No newline at end of file diff --git a/src/HeartTrackAPI/appsettings.json b/src/HeartTrackAPI/appsettings.json index 1178611..17437fa 100644 --- a/src/HeartTrackAPI/appsettings.json +++ b/src/HeartTrackAPI/appsettings.json @@ -9,9 +9,9 @@ "ConnectionStrings": { "HeartTrackAuthConnection": "Data Source=uca_HeartTrack.db" }, - "Jwt": { + "JWT": { "Issuer": "HeartTrack", "Audience": "HeartTrack", - "SigningKey": "ThisIsMySuperKey" + "SigningKey": "jythgfbhtgdfytrhdgfythrydfghtrydfythrjgfytjgfjkhljkloijijlijoukhjuhygyhdhcesxeqaewxsfcgevfbtgjnh61689346843njfdvcgdv" } }