Skip to content

Commit

Permalink
Merge branch 'feature/authorization' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
DustSwiffer committed Jul 21, 2024
2 parents 498fffe + 700a5e5 commit f700152
Show file tree
Hide file tree
Showing 23 changed files with 947 additions and 33 deletions.
1 change: 1 addition & 0 deletions AdvancedAPI.Business/AdvancedAPI.Business.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="AutoMapper" Version="13.0.1" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.1" />
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.556">
<PrivateAssets>all</PrivateAssets>
Expand Down
19 changes: 19 additions & 0 deletions AdvancedAPI.Business/MappingProfile.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using AdvancedAPI.Data.Models;
using AdvancedAPI.Data.ViewModels.NewsArticle;
using AutoMapper;

namespace Business;

/// <summary>
/// Auto mapper profiles.
/// </summary>
public class MappingProfile : Profile
{
/// <summary>
/// Mapping Models against entities and opposite.
/// </summary>
public MappingProfile()
{
CreateMap<NewsArticleRequestModel, NewsArticle>();
}
}
23 changes: 23 additions & 0 deletions AdvancedAPI.Business/ServiceExtension.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using Business.Services;
using Business.Services.Interfaces;

namespace Business;

/// <summary>
/// Service extension used to prepare the business layer for usage.
/// </summary>
public static class ServiceExtension
{
/// <summary>
/// Registers everything business layer related.
/// </summary>
public static IServiceCollection AddBusinessServices(this IServiceCollection services)
{
services.AddAutoMapper(typeof(MappingProfile).Assembly);

services.AddScoped<IAuthenticationService, AuthenticationService>();
services.AddScoped<INewsArticleService, NewsArticleService>();

return services;
}
}
4 changes: 3 additions & 1 deletion AdvancedAPI.Business/Services/AuthenticationService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,14 @@ public AuthenticationService(IIdentityRepository identityRepository, IConfigurat
IdentityUser? user = await _identityRepository.GetUser(requestModel.Username);
if (user != null && await _identityRepository.CheckPassword(user, requestModel.Password))
{
Claim[] authClaims = new[]
List<Claim> authClaims = new()
{
new Claim(JwtRegisteredClaimNames.Sub, user.UserName),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
};

IList<string> userRoles = await _identityRepository.GetRoles(user);
authClaims.AddRange(userRoles.Select(role => new Claim(ClaimTypes.Role, role)));
SymmetricSecurityKey authSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Jwt:Key"]));

JwtSecurityToken token = new JwtSecurityToken(
Expand Down
19 changes: 19 additions & 0 deletions AdvancedAPI.Business/Services/Interfaces/INewsArticleService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using AdvancedAPI.Data.ViewModels.NewsArticle;

namespace Business.Services.Interfaces;

/// <summary>
/// news article service.
/// </summary>
public interface INewsArticleService
{
/// <summary>
/// Inserts a new news article into the database.
/// </summary>
public Task<bool> CreateNewsArticle(NewsArticleRequestModel requestModel);

/// <summary>
/// Deletes the news article from the database.
/// </summary>
public Task<bool> DeleteNewsArticle(int id);
}
68 changes: 68 additions & 0 deletions AdvancedAPI.Business/Services/NewsArticleService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
using AdvancedAPI.Data.Models;
using AdvancedAPI.Data.Repositories.Interfaces;
using AdvancedAPI.Data.ViewModels.NewsArticle;
using AutoMapper;
using Business.Services.Interfaces;

namespace Business.Services;

/// <inheritdoc />
public class NewsArticleService : INewsArticleService
{
private readonly ILogger<NewsArticleService> _logger;
private readonly IMapper _mapper;
private readonly INewsArticleRepository _newsArticleRepository;

/// <summary>
/// Constructor.
/// </summary>
public NewsArticleService(ILogger<NewsArticleService> logger, IMapper mapper, INewsArticleRepository newsArticleRepository)
{
_logger = logger;
_mapper = mapper;
_newsArticleRepository = newsArticleRepository;
}

/// <inheritdoc />
public async Task<bool> CreateNewsArticle(NewsArticleRequestModel requestModel)
{
var mapped = _mapper.Map<NewsArticle>(requestModel);
mapped.ReleaseDate = DateTime.Now;

try
{
await _newsArticleRepository.AddAsync(mapped);
await _newsArticleRepository.SaveAsync();
}
catch (Exception e)
{
_logger.LogError(20, e, "Failed to insert the news article");
throw new Exception("Could not insert news article");
}

return true;
}

/// <inheritdoc />
public async Task<bool> DeleteNewsArticle(int id)
{
try
{
NewsArticle? newsArticle = await _newsArticleRepository.GetByIdAsync(id);
if (newsArticle != null)
{
_newsArticleRepository.Delete(newsArticle);
await _newsArticleRepository.SaveAsync();

return true;
}
}
catch (Exception e)
{
_logger.LogError(20, e, $"Failed to delete the news article with id: {id}");
throw new Exception($"Could not delete the news article with id: {id}");
}

return false;
}
}
24 changes: 23 additions & 1 deletion AdvancedAPI.Data/AdvancedApiContext.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Microsoft.AspNetCore.Identity;
using AdvancedAPI.Data.Models;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;

Expand All @@ -16,4 +17,25 @@ public AdvancedApiContext(DbContextOptions<AdvancedApiContext> options)
: base(options)
{
}

/// <summary>
/// News article database objects.
/// </summary>
public DbSet<NewsArticle> NewsArticles { get; set; }

/// <summary>
/// when creating models.
/// </summary>
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);

// Ensure primary keys are defined for IdentityUserLogin
modelBuilder.Entity<IdentityUserLogin<string>>(entity =>
{
entity.HasKey(e => new { e.LoginProvider, e.ProviderKey });
});

// Additional model configurations
}
}
27 changes: 25 additions & 2 deletions AdvancedAPI.Data/DbInitializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ public static async Task Initialize(IServiceProvider serviceProvider)
// Seed roles
await SeedRoles(roleManager);

// Seed admin user
// Seed users
await SeedAdminUser(userManager);
await SeedUserUser(userManager);
}

/// <summary>
Expand All @@ -41,7 +42,7 @@ private static async Task SeedRoles(RoleManager<IdentityRole> roleManager)
}

/// <summary>
/// Seeding user into the database.
/// Seeding admin user into the database.
/// </summary>
private static async Task SeedAdminUser(UserManager<IdentityUser> userManager)
{
Expand All @@ -61,5 +62,27 @@ private static async Task SeedAdminUser(UserManager<IdentityUser> userManager)
}
}
}

/// <summary>
/// Seeding user user into the database.
/// </summary>
private static async Task SeedUserUser(UserManager<IdentityUser> userManager)
{
IdentityUser? adminUser = await userManager.FindByEmailAsync("[email protected]");
if (adminUser == null)
{
adminUser = new IdentityUser
{
UserName = "[email protected]",
Email = "[email protected]",
};

IdentityResult? result = await userManager.CreateAsync(adminUser, "P@ssw0rd");
if (result.Succeeded)
{
await userManager.AddToRoleAsync(adminUser, "User");
}
}
}
}
}
Loading

0 comments on commit f700152

Please sign in to comment.