Skip to content

Commit

Permalink
Merge pull request #154 from Strayker-Software/feature/153-add-authen…
Browse files Browse the repository at this point in the history
…tication-and-authorization

Add authentication and authorization
  • Loading branch information
StraykerPL authored May 25, 2024
2 parents c80e6ee + c56c24d commit 5543f8f
Show file tree
Hide file tree
Showing 89 changed files with 4,399 additions and 1,367 deletions.
3 changes: 2 additions & 1 deletion binder-web-backend/Binder.Api/Binder.Api.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.15.1" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.3" />
<PackageReference Include="SimpleAuthenticationTools" Version="2.0.15" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
using Binder.Application.Models.Interfaces;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace Binder.Api.Controllers
{
[Route("api/versions")]
[ApiController]
[Authorize]
public sealed class AppVersionsController : ControllerBase
{
private readonly IAppVersionService _service;
Expand Down
42 changes: 42 additions & 0 deletions binder-web-backend/Binder.Api/Controllers/AuthController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using Binder.Api.Models;
using Binder.Api.Providers.Interfaces;
using Binder.Application.Services.Middleware;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using SimpleAuthentication.JwtBearer;
using System.Security.Authentication;

namespace Binder.Api.Controllers
{
[Route("api/auth")]
[ApiController]
public sealed class AuthController : ControllerBase
{
private readonly IJwtBearerService _jwtBearerService;
private readonly IAuthProvider _authProvider;

public AuthController(
IJwtBearerService bearerService,
IAuthProvider authProvider)
{
_jwtBearerService = bearerService;
_authProvider = authProvider;
}

[HttpPost]
[Route("login")]
[AllowAnonymous]
public ActionResult<LoginResponseDTO> Login(LoginRequestDTO loginRequest)
{
if (_authProvider.Authenticate(loginRequest.UserName, loginRequest.Password))
{
var token = _jwtBearerService.CreateToken(loginRequest.UserName);

return Ok(new LoginResponseDTO(token));
}

return Unauthorized(GetProblemDetailsByExceptionFactory
.GetProblemDetails(new InvalidCredentialException()));
}
}
}
25 changes: 21 additions & 4 deletions binder-web-backend/Binder.Api/Controllers/TablesController.cs
Original file line number Diff line number Diff line change
@@ -1,40 +1,57 @@
using AutoMapper;
using Binder.Api.Models;
using Binder.Api.Providers;
using Binder.Application.Models.Interfaces;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace Binder.Api.Controllers
{
[Route("api/tables")]
[ApiController]
[Authorize]
public sealed class TablesController : ControllerBase
{
private readonly IDefaultTableService _service;
private readonly IMapper _mapper;
private readonly JwtDataProvider _jwtDataProvider;

public TablesController(IDefaultTableService service, IMapper mapper)
public TablesController(
IDefaultTableService service,
JwtDataProvider provider,
IMapper mapper)
{
_service = service;
_mapper = mapper;
_jwtDataProvider = provider;
}

[Route("{tableId}")]
[HttpGet]
public ActionResult<DefaultTableDTO> Get(int tableId)
{
return _mapper.Map<DefaultTableDTO>(_service.GetTable(tableId));
var userName = _jwtDataProvider.GetUserNameFromToken(
Request.Headers["Authorization"]!.Single()!.Remove(0, 7)) ?? throw new Exception();

return _mapper.Map<DefaultTableDTO>(_service.GetTable(userName, tableId));
}

[HttpGet]
public ActionResult<DefaultTableDTO[]> Get()
{
return Ok(_mapper.Map<DefaultTableDTO[]>(_service.GetAllTables()));
var userName = _jwtDataProvider.GetUserNameFromToken(
Request.Headers["Authorization"]!.Single()!.Remove(0, 7)) ?? throw new Exception();

return Ok(_mapper.Map<DefaultTableDTO[]>(_service.GetAllTables(userName)));
}

[HttpPost]
public ActionResult Post(string tableName)
{
_service.CreateTable(tableName);
var userName = _jwtDataProvider.GetUserNameFromToken(
Request.Headers["Authorization"]!.Single()!.Remove(0, 7)) ?? throw new Exception();

_service.CreateTable(userName, tableName);

return Ok();
}
Expand Down
24 changes: 19 additions & 5 deletions binder-web-backend/Binder.Api/Controllers/ToDoTasksController.cs
Original file line number Diff line number Diff line change
@@ -1,43 +1,57 @@
using AutoMapper;
using Binder.Api.Models;
using Binder.Api.Providers;
using Binder.Application.Models.Interfaces;
using Binder.Core.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace Binder.Api.Controllers
{
[Route("api/tasks")]
[ApiController]
[Authorize]
public sealed class ToDoTasksController : ControllerBase
{
private readonly IToDoTasksService _service;
private readonly IMapper _mapper;
private readonly JwtDataProvider _jwtDataProvider;

public ToDoTasksController(IToDoTasksService service, IMapper mapper)
public ToDoTasksController(
IToDoTasksService service,
JwtDataProvider jwtDataProvider,
IMapper mapper)
{
_service = service;
_mapper = mapper;
_jwtDataProvider = jwtDataProvider;
}

[HttpGet]
public ActionResult<ToDoTaskDTO[]> Get(int tableId, TaskShow showFiltering)
{
var userName = _jwtDataProvider.GetUserNameFromToken(
Request.Headers["Authorization"]!.Single()!.Remove(0, 7)) ?? throw new Exception();

return showFiltering switch
{
TaskShow.ShowCompleted => Ok(
_mapper.Map<ToDoTaskDTO[]>(_service.GetTasksForTable(tableId).Where(x => x.IsCompleted == true))),
_mapper.Map<ToDoTaskDTO[]>(_service.GetTasksForTable(userName, tableId).Where(x => x.IsCompleted == true))),
TaskShow.HideCompleted => Ok(
_mapper.Map<ToDoTaskDTO[]>(_service.GetTasksForTable(tableId).Where(x => x.IsCompleted == false))),
_mapper.Map<ToDoTaskDTO[]>(_service.GetTasksForTable(userName, tableId).Where(x => x.IsCompleted == false))),
TaskShow.ShowAll => Ok(
_mapper.Map<ToDoTaskDTO[]>(_service.GetTasksForTable(tableId))),
_mapper.Map<ToDoTaskDTO[]>(_service.GetTasksForTable(userName, tableId))),
_ => NotFound(),
};
}

[HttpPost]
public ActionResult Post(ToDoTaskDTO task)
{
_service.AddTaskToTable(_mapper.Map<ToDoTask>(task));
var userName = _jwtDataProvider.GetUserNameFromToken(
Request.Headers["Authorization"]!.Single()!.Remove(0, 7)) ?? throw new Exception();

_service.AddTaskToTable(userName, _mapper.Map<ToDoTask>(task));

return Ok();
}
Expand Down
16 changes: 16 additions & 0 deletions binder-web-backend/Binder.Api/Extensions/AddProvidersExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using Binder.Api.Providers;
using Binder.Api.Providers.Interfaces;

namespace Binder.Api.Extensions
{
public static class AddProvidersExtensions
{
public static IServiceCollection AddProviders(this IServiceCollection services)
{
services.AddScoped<JwtDataProvider>();
services.AddScoped<IAuthProvider, UsernamePasswordAuthProvider>();

return services;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Binder.Api.Constants;
using Microsoft.OpenApi.Models;
using SimpleAuthentication;

namespace Binder.Api.Extensions
{
Expand All @@ -22,6 +23,8 @@ public static IServiceCollection AddSwaggerDocumentation(this IServiceCollection
{
Url = backendUrl
});

options.AddSimpleAuthentication(config);
});

return services;
Expand Down
4 changes: 4 additions & 0 deletions binder-web-backend/Binder.Api/Models/LoginRequestDTO.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
namespace Binder.Api.Models
{
public record class LoginRequestDTO(string UserName, string Password);
}
4 changes: 4 additions & 0 deletions binder-web-backend/Binder.Api/Models/LoginResponseDTO.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
namespace Binder.Api.Models
{
public record class LoginResponseDTO(string Token);
}
10 changes: 9 additions & 1 deletion binder-web-backend/Binder.Api/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Binder.Application.Services.Middleware;
using Binder.Persistence.Configurations;
using Binder.Persistence.Extensions;
using SimpleAuthentication;

namespace Binder.Api
{
Expand All @@ -17,14 +18,20 @@ public static void Main(string[] args)
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddMySqlConfig(builder.Configuration);
builder.Services.AddAutoMapperProfiles();
builder.Services.AddSimpleAuthentication(builder.Configuration);
builder.Services.AddProviders();
builder.Services.AddServices();
builder.Services.AddRepositories();
builder.Services.AddSwaggerDocumentation(builder.Configuration);

var app = builder.Build();

app.UseSwaggerDocumentation();
app.UseMiddleware<ExceptionHandlingMiddleware>();

if (app.Environment.IsProduction())
{
app.UseMiddleware<ExceptionHandlingMiddleware>();
}

string frontendUrl = builder.Configuration
.GetSection(WebApiIocConfigValues.FrontendUrlSectionKey).Value ?? throw new ArgumentNullException();
Expand All @@ -37,6 +44,7 @@ public static void Main(string[] args)
});

app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,6 @@
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Docker"
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Binder.Api.Providers.Interfaces
{
public interface IAuthProvider
{
bool Authenticate(string username, string password);
}
}
25 changes: 25 additions & 0 deletions binder-web-backend/Binder.Api/Providers/JwtDataProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using SimpleAuthentication.JwtBearer;
using System.Security.Claims;

namespace Binder.Api.Providers
{
public sealed class JwtDataProvider
{
private readonly IJwtBearerService _jwtBearerService;

public JwtDataProvider(IJwtBearerService bearerService)
{
_jwtBearerService = bearerService;
}

public string? GetUserNameFromToken(string token)
{
if (_jwtBearerService.TryValidateToken(token, true, out ClaimsPrincipal? principal) && principal is not null)
{
return principal.Identity!.Name!;
}

return null;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using Binder.Api.Providers.Interfaces;
using Binder.Application.Models.Interfaces;

namespace Binder.Api.Providers
{
public sealed class UsernamePasswordAuthProvider : IAuthProvider
{
private readonly IUsersService _usersService;

public UsernamePasswordAuthProvider(IUsersService usersService)
{
_usersService = usersService;
}

public bool Authenticate(string username, string password)
{
var passwordFromDb = _usersService.GetUserByName(username).Password;

return password == passwordFromDb;
}
}
}
14 changes: 11 additions & 3 deletions binder-web-backend/Binder.Api/appsettings.Development.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,17 @@
}
},
"ConnectionStrings": {
"DefaultConnection": "Server=localhost;Database=binder_db;Uid=root;Pwd=;"
},
"DefaultConnection": "Server=localhost;Database=binder_db;Uid=root;Pwd=;"
},
"MySqlServerVersion": "10.4.27",
"FrontendUrl": "http://localhost:4200",
"BackendUrl": "http://localhost:5246"
"BackendUrl": "http://localhost:5246",
"Authentication": {
"DefaultScheme": "Bearer",
"JwtBearer": {
"SchemeName": "Bearer",
"SecurityKey": ",jUv\\P]`2UL*dd5C9+f@<9P$bx(zR\\!(Yq<5zw$^]%rW4E[e^4+Y$WeD^Xoj~<{JVYew?.~$h#~o2S&dv@9<e)Es}U7h-sLyqs\\HCW`9v$Ji3XRUtjcA&LEj[U;reoPcz+syUckg(.jqv(D-RWnH9\\D/cg+*u7kWg<=!F-@4z<A4jssH>j:/?.@D>9^[AM`yMQKfXS]5$~M]qr+b,R)dM7HJF)<`$fk5arMR#p4-;(Ja=`[>{C\\:fe9.^eT?KX(u",
"ExpirationTime": "00:30:00"
}
}
}
2 changes: 1 addition & 1 deletion binder-web-backend/Binder.Api/appsettings.Docker.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
},
"ConnectionStrings": {
"DefaultConnection": "Server=localhost;Port=3306;Database=binder_db;User Id=newuser;Pwd=example;"
},
},
"MySqlServerVersion": "10.4.27",
"FrontendUrl": "http://localhost:4200",
"BackendUrl": "http://localhost:5246"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ public static class ApplicationServices
{
public static IServiceCollection AddServices(this IServiceCollection services)
{
services.AddScoped<IUsersService, UsersService>();
services.AddScoped<IDefaultTableService, DefaultTableService>();
services.AddScoped<IToDoTasksService, ToDoTasksService>();
services.AddScoped<IAppVersionService, AppVersionService>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ namespace Binder.Application.Models.Interfaces
{
public interface IDefaultTableService
{
DefaultTable GetTable(int tableId);
DefaultTable GetTable(string userName, int tableId);

ICollection<DefaultTable> GetAllTables();
ICollection<DefaultTable> GetAllTables(string userName);

DefaultTable CreateTable(string tableName);
DefaultTable CreateTable(string userName, string tableName);
}
}
Loading

0 comments on commit 5543f8f

Please sign in to comment.