Reputation: 460
I have implemented a custom authorization handler to determinate if user is authorized to edit or delete a ressource.
But the handler is never called and I get always a 401 status code when I try to edit a ressource.
This is my custom Handler :
public class MeetingAuthorizationHandler : AuthorizationHandler<SameCreatorRequirement, Meeting>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, SameCreatorRequirement requirement, Meeting resource)
{
/*if (context.User.Claims.Where(c => c.Type == ClaimTypes.NameIdentifier).FirstOrDefault() != null)
{
int idCreator = Int32.Parse(context.User.Claims.Where(c => c.Type == ClaimTypes.NameIdentifier).FirstOrDefault().Value);
if(idCreator == resource.CreatedBy.IdUserPreferences)
{
context.Succeed(requirement);
}
}*/
context.Succeed(requirement);
return Task.CompletedTask;
}
}
public class SameCreatorRequirement : IAuthorizationRequirement
{
}
I have also add these line in the configure method :
services.AddSingleton<IAuthorizationHandler, MeetingAuthorizationHandler>();
services.AddAuthorization(options =>
{
options.AddPolicy("Admin", policy => policy.RequireClaim("Admin"));
options.AddPolicy("EditPolicy", policy => policy.Requirements.Add(new SameCreatorRequirement()));
});
In controller, I use this :
[Route("Meetings/Edit/{id}")]
[Authorize(Policy = "EditPolicy")]
public async Task<IActionResult> Edit(int id)
I have read a lot of related post and I can find why it's not working. What's wrong in this code ?
I have found this at the output (I use Cookie Authentication without identity, see this)
Microsoft.AspNetCore.Authorization.DefaultAuthorizationService:Information: Authorization was successful.
Microsoft.AspNetCore.Authorization.DefaultAuthorizationService:Information: Authorization failed.
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker:Information: Authorization failed for the request at filter 'Microsoft.AspNetCore.Mvc.Authorization.AuthorizeFilter'.
Microsoft.AspNetCore.Mvc.ForbidResult:Information: Executing ForbidResult with authentication schemes ().
Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler:Information: AuthenticationScheme: Cookies was forbidden.
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker:Information: Executed action RoomScheduling.Controllers.MeetingsController.Edit (RoomScheduling) in 21.4851ms
This is my controller :
namespace RoomScheduling.Controllers
{
[Authorize]
public class MeetingsController : Controller
{
/** ------------------- **/
[Route("Meetings/Edit/{id}")]
[Authorize(Policy = "EditPolicy")]
public async Task<IActionResult> Edit(int id)
{
MeetingView meetingView = new MeetingView();
Meeting meeting = await _context.Meeting
.Include(m => m.Room)
.Include(m => m.MeetingType)
.Include(m => m.CreatedBy)
//.Include(m => m.UpdatedBy)
.Include(m => m.Attendees)
.AsNoTracking()
.SingleAsync(m => m.IdMeeting == id);
meeting.UpdatedBy = new UserPreferences();
meeting.UpdatedBy.IdUserPreferences = GetCurrentUserId();
meetingView.Meeting = meeting;
meetingView.Attendees = meeting.Attendees.Select(c => c.Attendant).ToArray();
ViewBag.RoomId = new SelectList(_context.Room.AsNoTracking().Include(m => m.Area).ToList(), "IdRoom", "Name", meeting.Room.IdRoom, "Area.Name"); //Dropdown list
ViewBag.MeetingTypeId = new SelectList(_context.MeetingType.AsNoTracking().ToList(), "IdMeetingType", "Name"); //Dropdown list
ViewBag.AttendeesIdList = new MultiSelectList(_context.Attendees.AsNoTracking().Select(a => a.Attendant).Distinct().ToList(), "", "", meetingView.Attendees);
return View(meetingView);
}
/** ------------------- **/
}
}
Upvotes: 2
Views: 5306
Reputation: 7215
The problem is that you are trying to authorize against a Resource. Your policy has no knowledge on how to retrieve or access the Meeting
Resource.
If you want to make decisions in your AuthorizationHandler
that need to access the Meeting
object you can inject the IAuthorizationService
into your controller and call authorize against the policy using your Meeting
Resource.
[Route("Meetings/Edit/{id}")]
[Authorize(Policy = "EditPolicy")]
public async Task<IActionResult> Edit(int id)
{
MeetingView meetingView = new MeetingView();
Meeting meeting = await _context.Meeting
.Include(m => m.Room)
.Include(m => m.MeetingType)
.Include(m => m.CreatedBy)
//.Include(m => m.UpdatedBy)
.Include(m => m.Attendees)
.AsNoTracking()
.SingleAsync(m => m.IdMeeting == id);
// Authorize the user against the EditPolicy using the meeting resource.
var result = await _authorizationService.AuthorizeAsync(User, meeting, "EditPolicy");
if (!result.Succeeded)
{
return Forbid();
}
// Do stuff.
}
If you wish to not make authorization decisions on the resource you can use a different AuthorizationHandler<TRequirement>
For example:
public class MeetingAuthorizationHandler : AuthorizationHandler<SameCreatorRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, SameCreatorRequirement requirement)
{
context.Succeed(requirement);
return Task.CompletedTask;
}
}
Upvotes: 2