diff --git a/URL-Shortener/Controllers/ShortUrlController.cs b/URL-Shortener/Controllers/ShortUrlController.cs index a688c67..84633cf 100644 --- a/URL-Shortener/Controllers/ShortUrlController.cs +++ b/URL-Shortener/Controllers/ShortUrlController.cs @@ -5,6 +5,7 @@ using URL_Shortener.Models; using HashidsNet; using Microsoft.AspNetCore.Authorization; using System.Security.Claims; +using URL_Shortener.Extensions; namespace URL_Shortener.Controllers { @@ -40,13 +41,15 @@ namespace URL_Shortener.Controllers } [HttpPost] + [Authorize] public async Task CreateShortUrlAsync(CreateShortUrlDto dto) { var shortUrl = new ShortUrl { OriginalUrl = dto.OriginalUrl, ExpirationDateTime = dto.ExpirationDateTime, - ClickLimit = dto.ClickLimit + ClickLimit = dto.ClickLimit, + UserId = int.Parse(User.FindFirstValue(ClaimTypes.NameIdentifier)!) }; await _db.ShortUrls.AddAsync(shortUrl); await _db.SaveChangesAsync(); @@ -57,12 +60,16 @@ namespace URL_Shortener.Controllers } [HttpPatch("{id:int}")] + [Authorize] public async Task UpdateShortUrlAsync(int id, UpdateShortUrlDto dto) { var shortUrl = await _db.ShortUrls.FindAsync(id); if (shortUrl == null) return NotFound("Short URL not found"); + if (!User.CompareId(shortUrl.UserId)) + throw new Exception("Not authorized"); + if (!string.IsNullOrEmpty(dto.OriginalUrl)) shortUrl.OriginalUrl = dto.OriginalUrl; if (dto.ExpirationDateTime.HasValue) @@ -87,8 +94,7 @@ namespace URL_Shortener.Controllers if (shortUrl == null) return NotFound(); - if (!int.TryParse(User.FindFirstValue(ClaimTypes.NameIdentifier), out var userId) || - shortUrl.UserId != userId) + if (!User.CompareId(shortUrl.UserId)) throw new Exception("Not authorized"); _db.ShortUrls.Remove(shortUrl); diff --git a/URL-Shortener/Extensions/ClaimsPrincipalExtensions.cs b/URL-Shortener/Extensions/ClaimsPrincipalExtensions.cs new file mode 100644 index 0000000..15bfa5a --- /dev/null +++ b/URL-Shortener/Extensions/ClaimsPrincipalExtensions.cs @@ -0,0 +1,12 @@ +using System.Security.Claims; + +namespace URL_Shortener.Extensions; + +public static class ClaimsPrincipalExtensions +{ + public static bool CompareId(this ClaimsPrincipal claimsPrincipal, int userId) + { + return int.TryParse(claimsPrincipal.FindFirstValue(ClaimTypes.NameIdentifier), out int id) && + id == userId; + } +} \ No newline at end of file diff --git a/URL-Shortener/Program.cs b/URL-Shortener/Program.cs index 2dcedc9..7855a0f 100644 --- a/URL-Shortener/Program.cs +++ b/URL-Shortener/Program.cs @@ -4,6 +4,7 @@ using URL_Shortener; using URL_Shortener.Services; using URL_Shortener.Settings; using Microsoft.IdentityModel.Tokens; +using Microsoft.OpenApi; using System.Text; var builder = WebApplication.CreateBuilder(args); @@ -15,7 +16,21 @@ builder.Services.AddControllers(); builder.Services.AddOpenApi(); builder.Services.AddEndpointsApiExplorer(); -builder.Services.AddSwaggerGen(); +builder.Services.AddSwaggerGen(options => +{ + 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)] = [] + }); +}); builder.Services.AddSingleton(); @@ -29,8 +44,8 @@ builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(o => o.TokenValidationParameters = new() { ValidateIssuerSigningKey = true, - //ValidateIssuer = true, ValidateAudience = false, + ValidateIssuer = false, ValidateLifetime = true, IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes( authSettings.Get()!.SecretKey)) @@ -61,8 +76,6 @@ using (var scope = app.Services.CreateScope()) retry.Execute(db.Database.Migrate); } -//app.UseHttpsRedirection(); - app.UseAuthentication(); app.UseAuthorization();