Skip to content

Commit

Permalink
Support for role-based authorization.
Browse files Browse the repository at this point in the history
  • Loading branch information
jasontaylordev committed Apr 22, 2020
1 parent f97485b commit 573f306
Show file tree
Hide file tree
Showing 6 changed files with 59 additions and 4 deletions.
41 changes: 37 additions & 4 deletions src/Application/Common/Behaviours/AuthorizationBehaviour.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,57 @@ public class AuthorizationBehaviour<TRequest, TResponse> : IPipelineBehavior<TRe
{
private readonly ILogger<TRequest> _logger;
private readonly ICurrentUserService _currentUserService;
private readonly IIdentityService _identityService;

public AuthorizationBehaviour(
ILogger<TRequest> logger,
ICurrentUserService currentUserService)
ICurrentUserService currentUserService,
IIdentityService identityService)
{
_logger = logger;
_currentUserService = currentUserService;
_identityService = identityService;
}

public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next)
{
var requiresAuthorization = request.GetType().GetCustomAttributes<AuthorizeAttribute>().Any();
var authorizeAttributes = request.GetType().GetCustomAttributes<AuthorizeAttribute>();

if (requiresAuthorization && _currentUserService.UserId == null)
if (!authorizeAttributes.Any())

This comment has been minimized.

Copy link
@o-l-i-g

o-l-i-g May 29, 2020

My first ever github comment on code! I'm sorry if I'm wrong but I think this should be testing for:

if (authorizeAttributes.Any()) 

instead of

if(authorizeAttributes.Any() == false)
{
throw new UnauthorizedAccessException();
// Must be authenticated user
if (_currentUserService.UserId == null)
{
throw new UnauthorizedAccessException();
}

var authorizeAttributesWithRoles = authorizeAttributes.Where(a => !string.IsNullOrWhiteSpace(a.Roles));

if (authorizeAttributesWithRoles.Any())
{
foreach (var roles in authorizeAttributesWithRoles.Select(a => a.Roles.Split(',')))
{
var authorized = false;
foreach (var role in roles)
{
var isInRole = await _identityService.UserIsInRole(_currentUserService.UserId, role.Trim());
if (isInRole)
{
authorized = true;
continue;
}
}

// Must be a member of at least one role in roles
if (!authorized)
{
throw new UnauthorizedAccessException();
}
}
}
}

// User is authorized / authorization not required
return await next();
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/Application/Common/Interfaces/IIdentityService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ public interface IIdentityService
{
Task<string> GetUserNameAsync(string userId);

Task<bool> UserIsInRole(string userId, string role);

Task<(Result Result, string UserId)> CreateUserAsync(string userName, string password);

Task<Result> DeleteUserAsync(string userId);
Expand Down
5 changes: 5 additions & 0 deletions src/Application/Common/Security/AuthorizeAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,10 @@ public class AuthorizeAttribute : Attribute
/// Initializes a new instance of the <see cref="AuthorizeAttribute"/> class.
/// </summary>
public AuthorizeAttribute() { }

/// <summary>
/// Gets or sets a comma delimited list of roles that are allowed to access the resource.
/// </summary>
public string Roles { get; set; }
}
}
2 changes: 2 additions & 0 deletions src/Infrastructure/DependencyInjection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using CleanArchitecture.Infrastructure.Persistence;
using CleanArchitecture.Infrastructure.Services;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
Expand All @@ -30,6 +31,7 @@ public static IServiceCollection AddInfrastructure(this IServiceCollection servi
services.AddScoped<IApplicationDbContext>(provider => provider.GetService<ApplicationDbContext>());

services.AddDefaultIdentity<ApplicationUser>()
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>();

services.AddIdentityServer()
Expand Down
7 changes: 7 additions & 0 deletions src/Infrastructure/Identity/IdentityService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ public async Task<string> GetUserNameAsync(string userId)
return (result.ToApplicationResult(), user.Id);
}

public async Task<bool> UserIsInRole(string userId, string role)
{
var user = _userManager.Users.SingleOrDefault(u => u.Id == userId);

return await _userManager.IsInRoleAsync(user, role);
}

public async Task<Result> DeleteUserAsync(string userId)
{
var user = _userManager.Users.SingleOrDefault(u => u.Id == userId);
Expand Down
6 changes: 6 additions & 0 deletions tests/Applicaton.IntegrationTests/Testing.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,16 @@ public static async Task<string> RunAsUserAsync(string userName, string password

var userManager = scope.ServiceProvider.GetService<UserManager<ApplicationUser>>();

var roleManager = scope.ServiceProvider.GetService<RoleManager<IdentityRole>>();

await roleManager.CreateAsync(new IdentityRole("Admin"));

var user = new ApplicationUser { UserName = userName, Email = userName };

var result = await userManager.CreateAsync(user, password);

await userManager.AddToRoleAsync(user, "Admin");

_currentUserId = user.Id;

return _currentUserId;
Expand Down

0 comments on commit 573f306

Please sign in to comment.