Skip to content

Commit

Permalink
Refactor AuthZ Handler
Browse files Browse the repository at this point in the history
  • Loading branch information
Sim committed Jun 21, 2022
1 parent d36081b commit 549c946
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 22 deletions.
7 changes: 2 additions & 5 deletions LeaderboardBackend/Authorization/MiddlewareResultHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,8 @@ public async Task HandleAsync(
AuthorizationPolicy authorizationPolicy,
PolicyAuthorizationResult policyAuthorizationResult)
{
if (policyAuthorizationResult.Forbidden)
{
httpContext.Response.StatusCode = (int)HttpStatusCode.NotFound;
return;
}
// FIXME: For now, we're doing a simple pass-through. In the future we'd want to
// conditionally return a redirect to the login page. -zysim

// Fallback to the default implementation.
// This can mean calling the controller action itself.
Expand Down
61 changes: 44 additions & 17 deletions LeaderboardBackend/Authorization/UserTypeAuthorizationHandler.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using LeaderboardBackend.Models.Entities;
using LeaderboardBackend.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.IdentityModel.Tokens;

namespace LeaderboardBackend.Authorization;

// FIXME: This should be reimplemented with the role-based authz thingy:
// https://github.com/leaderboardsgg/leaderboard-backend/issues/104
// - zysim
public class UserTypeAuthorizationHandler : AuthorizationHandler<UserTypeRequirement>
{
private readonly JwtSecurityTokenHandler JwtHandler;
Expand Down Expand Up @@ -35,40 +39,51 @@ UserTypeRequirement requirement
{
if (!TryGetJwtFromHttpContext(context, out string token) || !ValidateJwt(token))
{
return Task.CompletedTask;
return Fail(context);
}

Guid? userId = AuthService.GetUserIdFromClaims(context.User);
if (userId is null)
{
context.Fail();
return Task.CompletedTask;
return Fail(context);
}

User? user = UserService.GetUserById(userId.Value).Result;
if (user is null)
{
return Fail(context);
}

AddRoleClaimToContext(user, context);

if (user is null || !Handle(user, context, requirement))
if (!IsAuthorized(context, requirement))
{
// FIXME: Work out how to fail as a ForbiddenResult.
context.Fail();
return Task.CompletedTask;
return Fail(context);
}

context.Succeed(requirement);

return Task.CompletedTask;
}

private bool Handle(
User user,
AuthorizationHandlerContext _,
UserTypeRequirement requirement
) => requirement.Type switch
private void AddRoleClaimToContext(User user, AuthorizationHandlerContext context)
{
UserTypes.Admin => user.Admin,
UserTypes.Mod => user.Admin || IsMod(user),
UserTypes.User => true,
_ => false,
};
if (user.Admin)
{
context.User.AddIdentity(new(new Claim[] { new("role", UserTypes.Admin) }));
return;
}
if (IsMod(user))
{
context.User.AddIdentity(new(new Claim[] { new("role", UserTypes.Mod) }));
return;
}
}

private bool IsAuthorized(
AuthorizationHandlerContext context,
UserTypeRequirement requirement
) => context.User.HasClaim("role", requirement.Type) || context.User.HasClaim("role", UserTypes.Admin);

private bool IsMod(User user) => ModshipService.LoadUserModships(user.Id).Result.Count() > 0;

Expand Down Expand Up @@ -109,4 +124,16 @@ out _
return false;
}
}

private Task Fail(AuthorizationHandlerContext context, string? reason = null)
{
if (reason is not null)
{
context.Fail(new(this, reason));
} else
{
context.Fail();
}
return Task.CompletedTask;
}
}
1 change: 1 addition & 0 deletions LeaderboardBackend/Controllers/ParticipationsController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ public async Task<ActionResult> CreateParticipation([FromBody] CreateParticipati

/// <summary>Updates the participation of a user for a run.</summary>
/// <remarks>Expects both a comment and a VoD link.</remarks>
/// <param name="id">The run ID.</param>
/// <param name="request">The request body.</param>
/// <response code="200">A successful update.</response>
/// <response code="404">The participation could not be found.</response>
Expand Down

0 comments on commit 549c946

Please sign in to comment.