From 040170366bf14abe11e77061b20e1b9004b35770 Mon Sep 17 00:00:00 2001 From: sxergiu Date: Sun, 10 Dec 2023 16:43:21 +0200 Subject: [PATCH 1/2] Added KeyCloak userid retrieval + fixes --- .../Extensions/ClaimsPrincipalExtensions.cs | 18 +++++ GdscBackend/Database/IRepository.cs | 6 +- GdscBackend/Database/Repository.cs | 8 +-- .../Features/Articles/ArticleController.cs | 68 +++++++++++-------- .../Features/Events/EventsController.cs | 2 +- GdscBackend/GdscBackend.csproj | 4 -- .../template.appsettings.Development.json | 11 --- 7 files changed, 64 insertions(+), 53 deletions(-) create mode 100644 GdscBackend/Common/Extensions/ClaimsPrincipalExtensions.cs delete mode 100644 GdscBackend/template.appsettings.Development.json diff --git a/GdscBackend/Common/Extensions/ClaimsPrincipalExtensions.cs b/GdscBackend/Common/Extensions/ClaimsPrincipalExtensions.cs new file mode 100644 index 0000000..65c3753 --- /dev/null +++ b/GdscBackend/Common/Extensions/ClaimsPrincipalExtensions.cs @@ -0,0 +1,18 @@ +using System.Security.Claims; + +namespace GdscBackend.Common.Extensions; + +public static class ClaimsPrincipalExtensions +{ + public static string? GetUserId(this ClaimsPrincipal user) + { + var userIdClaim = user.Claims.FirstOrDefault(c => c.Type == "sub"); + + if (userIdClaim is not null && !string.IsNullOrEmpty(userIdClaim.Value)) + { + return userIdClaim.Value; + } + + return null; + } +} \ No newline at end of file diff --git a/GdscBackend/Database/IRepository.cs b/GdscBackend/Database/IRepository.cs index e2476a2..2adca32 100644 --- a/GdscBackend/Database/IRepository.cs +++ b/GdscBackend/Database/IRepository.cs @@ -8,10 +8,10 @@ public interface IRepository where T : class, IModel { DbSet DbSet { get; } Task AddAsync([NotNull] T entity); - Task GetAsync([NotNull] string id); + Task GetAsync([NotNull] string id); Task> GetAsync(); Task AddOrUpdateAsync([NotNull] T entity); - Task UpdateAsync([NotNull] string id ,[NotNull] object entity); - Task? DeleteAsync([NotNull] string id); + Task UpdateAsync([NotNull] string id ,[NotNull] object entity); + Task DeleteAsync([NotNull] string id); Task> DeleteAsync([NotNull] IEnumerable ids); } \ No newline at end of file diff --git a/GdscBackend/Database/Repository.cs b/GdscBackend/Database/Repository.cs index 98da45c..b0a1c83 100644 --- a/GdscBackend/Database/Repository.cs +++ b/GdscBackend/Database/Repository.cs @@ -34,21 +34,21 @@ public async Task> GetAsync() return await DbSet.ToListAsync(); } - public async Task GetAsync([NotNull] string id) + public async Task GetAsync([NotNull] string id) { return await DbSet.FirstOrDefaultAsync(e => e.Id == id); } public async Task AddOrUpdateAsync([NotNull] T entity) { - if (entity is null) return null; + // if (entity is null) return null; var existing = await DbSet.FirstOrDefaultAsync(item => item.Id == entity.Id); - + return existing is null ? await AddAsync(entity) : await UpdateAsync(entity.Id, entity); } - public async Task UpdateAsync([NotNull] string id, [NotNull] object newEntity) + public async Task UpdateAsync([NotNull] string id, [NotNull] object newEntity) { var entity = await GetAsync(id); if (entity is null) return null; diff --git a/GdscBackend/Features/Articles/ArticleController.cs b/GdscBackend/Features/Articles/ArticleController.cs index 2929629..6a0f347 100644 --- a/GdscBackend/Features/Articles/ArticleController.cs +++ b/GdscBackend/Features/Articles/ArticleController.cs @@ -1,4 +1,7 @@ -using GdscBackend.Database; +using System.Xml.Linq; +using GdscBackend.Common.Extensions; +using GdscBackend.Database; +using GdscBackend.Utils; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; @@ -7,18 +10,20 @@ namespace GdscBackend.Features.Articles; [ApiController] [ApiVersion("1")] -[Authorize("CoreTeam")] +[Authorize(AuthorizeConstants.CoreTeam)] [Route("v1/Articles")] public class ArticleController : ControllerBase { private readonly AppDbContext _dbContext; + private readonly IRepository _repo; - public ArticleController(AppDbContext appDbContext) + public ArticleController(AppDbContext appDbContext,IRepository repo) { _dbContext = appDbContext; + _repo = repo; } - /* + [HttpPost] [ProducesResponseType(StatusCodes.Status201Created)] [ProducesResponseType(StatusCodes.Status404NotFound)] @@ -26,13 +31,17 @@ public ArticleController(AppDbContext appDbContext) public async Task> Post(ArticleRequest request) { + var author = User.GetUserId(); - //var author = await _dbContext.Users.FirstOrDefaultAsync(entity => entity.Id == request.AuthorId); + return Ok(User.Claims.ToString()); + if (author is null) { return NotFound("User not found!"); } + return Ok(author); + var article = new ArticleModel { Id = Guid.NewGuid().ToString(), @@ -48,7 +57,7 @@ public async Task> Post(ArticleRequest request) return Created("v1/Articles", result.Entity); } - */ + [HttpGet] [AllowAnonymous] @@ -56,15 +65,14 @@ public async Task> Post(ArticleRequest request) [ProducesResponseType(StatusCodes.Status400BadRequest)] public async Task>> Get() { - var result = _dbContext.Articles.Select( - article => new ArticleResponse - { - Id = article.Id, - Created = article.Created, - Title = article.Title, - Content = article.Content, - AuthorId = article.AuthorId - }).ToList(); + var result = await _repo.DbSet.Select(article => new ArticleResponse + { + Id = article.Id, + Created = article.Created, + Title = article.Title, + Content = article.Content, + AuthorId = article.AuthorId + }).ToListAsync(); return Ok(result); } @@ -76,12 +84,13 @@ public async Task>> Get() [ProducesResponseType(StatusCodes.Status401Unauthorized)] public async Task> Delete([FromRoute] string id) { - var articol = await _dbContext.Articles.FirstOrDefaultAsync(entity => entity.Id == id); + + var articol = await _repo.GetAsync(id); + if (articol is null) return NotFound("Article not found!"); + var result = await _repo.DeleteAsync(articol.Id); - var result = _dbContext.Articles.Remove(articol); - await _dbContext.SaveChangesAsync(); - return Ok(result.Entity); + return Ok(result); } @@ -90,22 +99,21 @@ public async Task> Delete([FromRoute] string id) [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] - public async Task> ChangeAuthor([FromRoute] string id, [FromBody] string authorid) + public async Task> ChangeAuthor([FromRoute] string id) { - var article = await _dbContext.Articles.FirstOrDefaultAsync(entity => entity.Id == id); + var article = await _repo.GetAsync(id); if (article is null) return NotFound("Article not found!"); - /* check from keycloak - if (author is null) + var authorid = User.GetUserId(); + + if (authorid is null) { return NotFound("Author not found!"); } - */ article.AuthorId = authorid; article.Updated = DateTime.UtcNow; - - await _dbContext.SaveChangesAsync(); + return Ok(new ArticleResponse { Id = article.Id, @@ -123,12 +131,12 @@ public async Task> ChangeAuthor([FromRoute] string [ProducesResponseType(StatusCodes.Status401Unauthorized)] public async Task> ChangeContent([FromRoute] string id, [FromBody] string content) { - var article = await _dbContext.Articles.FirstOrDefaultAsync(entity => entity.Id == id); + var article = await _repo.GetAsync(id); if (article is null) return NotFound("Article not found!"); article.Content = content; article.Updated = DateTime.UtcNow; - await _dbContext.SaveChangesAsync(); + return Ok(new ArticleResponse { Id = article.Id, @@ -146,12 +154,12 @@ public async Task> ChangeContent([FromRoute] strin [ProducesResponseType(StatusCodes.Status401Unauthorized)] public async Task> ChangeTitle([FromRoute] string id, [FromBody] string title) { - var article = await _dbContext.Articles.FirstOrDefaultAsync(entity => entity.Id == id); + var article = await _repo.GetAsync(id); if (article is null) return NotFound("Article not found!"); article.Title = title; article.Updated = DateTime.UtcNow; - await _dbContext.SaveChangesAsync(); + return Ok(new ArticleResponse { Id = article.Id, diff --git a/GdscBackend/Features/Events/EventsController.cs b/GdscBackend/Features/Events/EventsController.cs index 6375049..fd52133 100644 --- a/GdscBackend/Features/Events/EventsController.cs +++ b/GdscBackend/Features/Events/EventsController.cs @@ -43,7 +43,7 @@ public async Task> Post(EventRequest entity) [ProducesResponseType(StatusCodes.Status201Created)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task> Delete([FromRoute] string id) + public async Task> Delete([FromRoute] string id) { var entity = await _repository.DeleteAsync(id); return entity is null ? NotFound() : Ok(entity); diff --git a/GdscBackend/GdscBackend.csproj b/GdscBackend/GdscBackend.csproj index cd0ce6e..c615cd1 100644 --- a/GdscBackend/GdscBackend.csproj +++ b/GdscBackend/GdscBackend.csproj @@ -27,8 +27,4 @@ - - - - diff --git a/GdscBackend/template.appsettings.Development.json b/GdscBackend/template.appsettings.Development.json deleted file mode 100644 index 0ccd4c0..0000000 --- a/GdscBackend/template.appsettings.Development.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Information" - } - }, - "ConnectionStrings": { - "Default": "Host=gdscupt.tech;Database=myDataBase;Username=guest;Password=myPassword;Port=6969;" - } -} From dc077e91e3eea4f0448ef1e9542614f4d61e9033 Mon Sep 17 00:00:00 2001 From: sxergiu Date: Sun, 10 Dec 2023 20:48:03 +0200 Subject: [PATCH 2/2] ArticleController interface implementation --- .../Extensions/ClaimsPrincipalExtensions.cs | 11 +---- GdscBackend/Database/Repository.cs | 2 +- .../Features/Articles/ArticleController.cs | 45 +++++++++---------- .../Features/Articles/ArticleRequest.cs | 2 - GdscBackend/GdscBackend.csproj | 16 +++++++ 5 files changed, 39 insertions(+), 37 deletions(-) diff --git a/GdscBackend/Common/Extensions/ClaimsPrincipalExtensions.cs b/GdscBackend/Common/Extensions/ClaimsPrincipalExtensions.cs index 65c3753..877cd8a 100644 --- a/GdscBackend/Common/Extensions/ClaimsPrincipalExtensions.cs +++ b/GdscBackend/Common/Extensions/ClaimsPrincipalExtensions.cs @@ -6,13 +6,6 @@ public static class ClaimsPrincipalExtensions { public static string? GetUserId(this ClaimsPrincipal user) { - var userIdClaim = user.Claims.FirstOrDefault(c => c.Type == "sub"); - - if (userIdClaim is not null && !string.IsNullOrEmpty(userIdClaim.Value)) - { - return userIdClaim.Value; - } - - return null; + return user.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier)?.Value; } -} \ No newline at end of file +} diff --git a/GdscBackend/Database/Repository.cs b/GdscBackend/Database/Repository.cs index b0a1c83..f961cdd 100644 --- a/GdscBackend/Database/Repository.cs +++ b/GdscBackend/Database/Repository.cs @@ -60,7 +60,7 @@ public async Task AddOrUpdateAsync([NotNull] T entity) return _dbSet.First(e => e.Id == id); } - public async Task? DeleteAsync([NotNull] string id) + public async Task DeleteAsync([NotNull] string id) { var entity = await DbSet.FirstOrDefaultAsync(item => item.Id == id); diff --git a/GdscBackend/Features/Articles/ArticleController.cs b/GdscBackend/Features/Articles/ArticleController.cs index 6a0f347..43ebeb0 100644 --- a/GdscBackend/Features/Articles/ArticleController.cs +++ b/GdscBackend/Features/Articles/ArticleController.cs @@ -1,4 +1,5 @@ -using System.Xml.Linq; +using System.Security.Claims; +using System.Xml.Linq; using GdscBackend.Common.Extensions; using GdscBackend.Database; using GdscBackend.Utils; @@ -14,15 +15,12 @@ namespace GdscBackend.Features.Articles; [Route("v1/Articles")] public class ArticleController : ControllerBase { - private readonly AppDbContext _dbContext; private readonly IRepository _repo; - public ArticleController(AppDbContext appDbContext,IRepository repo) + public ArticleController( IRepository repo ) { - _dbContext = appDbContext; _repo = repo; } - [HttpPost] [ProducesResponseType(StatusCodes.Status201Created)] @@ -31,16 +29,11 @@ public ArticleController(AppDbContext appDbContext,IRepository rep public async Task> Post(ArticleRequest request) { - var author = User.GetUserId(); - - return Ok(User.Claims.ToString()); - - if (author is null) - { + var authorid = User.GetUserId(); + if (authorid is null) + { return NotFound("User not found!"); } - - return Ok(author); var article = new ArticleModel { @@ -49,13 +42,12 @@ public async Task> Post(ArticleRequest request) Updated = DateTime.UtcNow, Title = request.Title, Content = request.Content, - //Author = author --> author from keycloak + AuthorId = authorid }; + + var result = await _repo.AddAsync(article); - var result = await _dbContext.Articles.AddAsync(article); - await _dbContext.SaveChangesAsync(); - - return Created("v1/Articles", result.Entity); + return Created("v1/Articles", result); } @@ -101,18 +93,16 @@ public async Task> Delete([FromRoute] string id) [ProducesResponseType(StatusCodes.Status401Unauthorized)] public async Task> ChangeAuthor([FromRoute] string id) { - var article = await _repo.GetAsync(id); + var article = await _repo.GetAsync(id); if (article is null) return NotFound("Article not found!"); var authorid = User.GetUserId(); - - if (authorid is null) - { - return NotFound("Author not found!"); - } + if (authorid is null) return NotFound("Author not found!"); article.AuthorId = authorid; article.Updated = DateTime.UtcNow; + + await _repo.UpdateAsync(article.Id,article); return Ok(new ArticleResponse { @@ -120,7 +110,7 @@ public async Task> ChangeAuthor([FromRoute] string Created = article.Created, Title = article.Title, Content = article.Content, - AuthorId = article.AuthorId + AuthorId = article.AuthorId // from keycloak }); } @@ -136,6 +126,8 @@ public async Task> ChangeContent([FromRoute] strin article.Content = content; article.Updated = DateTime.UtcNow; + + await _repo.UpdateAsync(article.Id, article); return Ok(new ArticleResponse { @@ -159,6 +151,9 @@ public async Task> ChangeTitle([FromRoute] string article.Title = title; article.Updated = DateTime.UtcNow; + + await _repo.UpdateAsync(article.Id, article); + return Ok(new ArticleResponse { diff --git a/GdscBackend/Features/Articles/ArticleRequest.cs b/GdscBackend/Features/Articles/ArticleRequest.cs index bb14bfb..ab8464d 100644 --- a/GdscBackend/Features/Articles/ArticleRequest.cs +++ b/GdscBackend/Features/Articles/ArticleRequest.cs @@ -7,6 +7,4 @@ public class ArticleRequest [Required]public string Title { get; set; } public string Content { get; set; } - - [Required]public string AuthorId { get; set; } } \ No newline at end of file diff --git a/GdscBackend/GdscBackend.csproj b/GdscBackend/GdscBackend.csproj index c615cd1..893f2d2 100644 --- a/GdscBackend/GdscBackend.csproj +++ b/GdscBackend/GdscBackend.csproj @@ -27,4 +27,20 @@ + + + + + + + + + + + + + + + +