Hei.
I have noticed a strange/unexpected behaviour in attribute-based role-permissions checks.
Specifically, it seems that every HTTP request to an authentication-required endpoint polls the UserAuthRole table repeatedly, rather than simply checking the session if/when available. I dug into the logic and believe that the method below is the cause, and I wonder, is this behaviour intention, or accidental and suboptimal? For reference, I am using sessions and not jwts
(copied from AuthUserSession.cs)
// Dale Comment: Current framework method definition
public override async Task<bool> HasRoleAsync(string role, IAuthRepositoryAsync authRepo, CancellationToken token=default)
{
// // Dale Comment: I am using sessions, so FromToken is false
if (!FromToken) //If populated from a token it should have the complete list of roles
{
// Dale Comment: this ALWAYS hits the database for every potential role in the RequiresRoleAttribute until detecting a match, resulting in several db calls per request
if (authRepo is IManageRolesAsync managesRoles)
{
if (UserAuthId == null)
{
return false;
}
return await managesRoles.HasRoleAsync(this.UserAuthId, role, token);
}
}
// Dale Comment: whereas the session already contains a Roles collection and I think we could check the session first and THEN hit the db if necessary??
// Dale Comment: Should/could this final check be moved ABOVE the database calls?
return this.Roles != null && this.Roles.Contains(role);
}
I noticed this accidentally while I was setting up logging and accidentally set to DEBUG level… at which point I noticed the following pattern [cache query, loop through roles until matches final role “MyRole2”]
[10:25:40 CorrelationId: 2cf8d60c-210f-4133-99c0-a89d24c5e670 DBG] SQL: SELECT "Id", "Data", "ExpiryDate", "CreatedDate", "ModifiedDate" FROM "CacheEntry" WHERE "Id" = @Id
PARAMS: Id=urn:iauthsession:QEX4IosqYyee2cv1IIwK
[10:25:40 CorrelationId: 2cf8d60c-210f-4133-99c0-a89d24c5e670 DBG] SQL: SELECT COUNT(*)
FROM "UserAuthRole"
WHERE (("UserAuthId" = @0) AND ("Role" = @1))
PARAMS: @0=14, @1=Admin
[10:25:40 CorrelationId: 2cf8d60c-210f-4133-99c0-a89d24c5e670 DBG] SQL: SELECT COUNT(*)
FROM "UserAuthRole"
WHERE (("UserAuthId" = @0) AND ("Role" = @1))
PARAMS: @0=14, @1=MyRole1
[10:25:40 CorrelationId: 2cf8d60c-210f-4133-99c0-a89d24c5e670 DBG] SQL: SELECT COUNT(*)
FROM "UserAuthRole"
WHERE (("UserAuthId" = @0) AND ("Role" = @1))
PARAMS: @0=14, @1=MyRole2
where the user logged in has the MyRole2 role (ie the final one being checked) and the request message class is declared as follows:
[RequiresAnyRole(nameof(MergeroRoleEnum.MyRole1), nameof(MergeroRoleEnum.MyRole2))]
[Route(
"/offerings/grid-content",
HttpMethods.Get,
Summary = "",
Notes = ""
)]
public class GetOfferingsGridContentQuery : IReturn<GetOfferingsGridContentResponse> { }