When dealing with Razor Pages and a Page Model class is the constructor called on each request? - asp.net-core

Something I have struggled with is understanding the lifecycle of the Page Model class for my Razor Pages usages. I'm trying to think about how and when to deal with common data I pass to my business logic like the userId that is running the request. So many times I need to save this information with the results of the action.
So where is the best place to handle something over and over like geting User details that might be in the persistence model and not in the context of the page model's User from the HTTPContext?
I should mention I am using authorize tags with cookie backed authenication to a webservice.

For accessing Reuqest from other layers except the Razor Page, you could try IHttpContextAccessor.
For general way to handling user details from request, you could create a service like below:
public interface IUserService
{
IdentityUser GetUser();
}
public class UserService:IUserService
{
private readonly ApplicationDbContext _context;
private readonly HttpContext _httpContext;
public UserService(ApplicationDbContext context
, IHttpContextAccessor httpContextAccessor)
{
_context = context;
_httpContext = httpContextAccessor.HttpContext;
}
public IdentityUser GetUser()
{
StringValues userId = "";
if (_httpContext.Request.Headers.TryGetValue("userId", out userId))
{
var user = _context.Users.FirstOrDefault(u => u.Id == userId);
return user;
}
else
{
return null;
}
}
}
And then register like
services.AddScoped<IUserService, UserService>();
Then, you could resolve IUserService from DI to use them when you want to access user info.
public class IndexModel : PageModel
{
private readonly IUserService _userService;
public IndexModel(IUserService userService)
{
_userService = userService;
}
public void OnGet()
{
var user = _userService.GetUser();
}
}

Related

Resource based authorization in SignalR

I have web API with custom policies and authorization handlers.
I wanted to reuse authorization handlers but HttpContext is null when attribute is used on signalr's hub.
For example this is my controller.
[Authorize]
public sealed class ChatsController : ControllerBase
{
[HttpPost("{chatId}/messages/send")]
[Authorize(Policy = PolicyNames.ChatParticipant)]
public Task SendMessage() => Task.CompletedTask;
}
And this my my authorization handler. I can extract "chatId" from HttpContext and then use my custom logic to authorize user.
internal sealed class ChatParticipantRequirementHandler : AuthorizationHandler<ChatParticipantRequirement>
{
private readonly IHttpContextAccessor _httpContextAccessor;
public ChatParticipantRequirementHandler(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, ChatParticipantRequirement requirement)
{
if(_httpContextAccessor.HttpContext != null)
{
// Logic
}
return Task.CompletedTask;
}
}
However this won't work with Azure SignalR because I don't have access to HttpContext. I know that I can provide custom IUserIdProvider but I have no idea how to access "chatId" from "Join" method in my custom authorization handler.
[Authorize]
public sealed class ChatHub : Hub<IChatClient>
{
[Authorize(Policy = PolicyNames.ChatParticipant)]
public async Task Join(Guid chatId)
{
await Groups.AddToGroupAsync(Context.ConnectionId, chatId.ToString());
}
Is it possible to reuse my authorization handlers?
I would like to avoid copypasting my code.
One solution is to extract my authorization code to separate services but then I have to manually call those from my hubs and abandon [Authorize] way.
Your chat is a resource, and you want to use resource based authorization. In this case declarative authorization with an attribute is not enough, because chat id is known at runtime only. So you have to use imperative authorization with IAuthorizationService.
Now in your hub:
[Authorize]
public sealed class ChatHub : Hub<IChatClient>
{
private readonly IAuthorizationService authService;
public ChatHub(IAuthorizationService authService)
{
this.authService = authService;
}
public async Task Join(Guid chatId)
{
// Get claims principal from authorized hub context
var user = this.Context.User;
// Get chat from DB or wherever you store it, or optionally just pass the ID to the authorization service
var chat = myDb.GetChatById(chatId);
var validationResult = await this.authService.AuthorizeAsync(user, chat, PolicyNames.ChatParticipant);
if (validationResult.Succeeded)
{
await Groups.AddToGroupAsync(Context.ConnectionId, chatId.ToString());
}
}
}
Your authorization handler should look different, because it needs the chat resource in its signature to do this kind of evaluation:
internal sealed class ChatParticipantRequirementHandler : AuthorizationHandler<ChatParticipantRequirement, Chat>
{
private readonly IHttpContextAccessor _httpContextAccessor;
public ChatParticipantRequirementHandler(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, ChatParticipantRequirement requirement, Chat chat)
{
// You have both user and chat now
var user = context.User;
if (this.IsMyUserAuthorizedToUseThisChat(user, chat))
{
context.Succeed(requirement);
}
else
{
context.Fail();
}
return Task.CompletedTask;
}
}
Edit: there is actually another option I didn't know about
You can make use of HubInvocationContext that SignalR Hub provides for authorized methods. This can be automatically injected into your AuthorizationHandler, which should look like this:
public class ChatParticipantRequirementHandler : AuthorizationHandler<ChatParticipantRequirement, HubInvocationContext>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, ChatParticipantRequirement requirement, HubInvocationContext hubContext)
{
var chatId = Guid.Parse((string)hubContext.HubMethodArguments[0]);
}
}
Hub method will be decorated normally with [Authorize(Policy = PolicyNames.ChatParticipant)]
You still will have two authorization handlers, AuthorizationHandler<ChatParticipantRequirement> and AuthorizationHandler<ChatParticipantRequirement, HubInvocationContext>, no way around it. As for code dublication, you can however just get the Chat ID in the handler, either from HttpContext or HubInvocationContext, and than pass it to you custom written MyAuthorizer that you could inject into both handlers:
public class MyAuthorizer : IMyAuthorizer
{
public bool CanUserChat(Guid userId, Guid chatId);
}

How can I make an ASP Core Claims Principal available in a non-controller object?

I am working on an ASP Core 2 project using JWT authentication and the Dapper ORM.
Like all ASP projects, I have a lot of controllers, each instantiating its associated data objects. Each data object inherits from an abstract DbObject class that provides database access services. I also have an AuthenticatedUser object that abstracts the JWT to make it's properties easier to use.
What I want is to do is create the AuthenticatedUser object in the constructor of DbObject. Of course, one method is to create it in the controller and pass it to every concrete data object but this is messy as it would have to be passed hundreds of times (and it just feels wrong).
Is there a way to use the ASP Core middleware to get the token after authentication and make it available through dependency injection in the DbObject?
Edit
Hopefully, this clarifies my intentions. I would like the controller to create data objects and use their properties and methods without regard to implementation (i.e. DbObject). But queries executed by DbObject will be filtered by information in the token of the logged in user.
public class ManufacturerController : Controller {
[HttpGet]
public async Task<IActionResult> Get() {
var manufacturers = await new Manufacturer().SelectMany();
return Ok(manufacturers);
}
[HttpGet("{id}")]
public async Task<IActionResult> Get(int id) {
var manufacturer = await new Manufacturer().SelectOne(id);
return Ok(manufacturer);
}...
public class Manufacturer : DbObject<Manufacturer> {
protected override string QrySelectOne => #"
Select *
From org.fn_Manufacturers ({0})
Where Id = {1}";
protected override string QrySelectMany => #"
Select *
From org.fn_Manufacturers ({0})";
public int Id { get; set; }
public string Name { get; set; }
public string Phone { get; set; }...
public abstract class DbObject<T> {
protected readonly AuthenticatedUser authenticatedUser;
public DbObject(IHttpContextAccessor contextAccessor) {
authenticatedUser = new
AuthenticatedUser(contextAccessor.HttpContext.User);
}
protected abstract string QrySelectOne { get; }
protected abstract string QrySelectMany { get; }
public async Task<T> SelectOne (int id) {...}
public async Task<T> SelectOne(params object[] ids) {...}
public async Task<IEnumerable<T>> SelectMany () {...}
public async Task<IEnumerable<T>> SelectMany (params object[] ids) {...}
I suppose one solution may be to create a static data object factory which has the IHttpContextAccessor injected??
ASP.NET Core provides IHttpContextAccessor interface for accessing HttpContext from non-controller objects.
The usage is fair simple. Inject IHttpContextAccessor into DbObject and access HttpContext by calling IHttpContextAccessor.HttpContext:
public abstract class DbObject
{
protected DbObject(IHttpContextAccessor contextAccessor)
{
var context = contextAccessor.HttpContext;
// Create instance of AuthenticatedUser based on context.User or other request data
}
}
EDIT
Your controllers instantiate data objects directly (with new operator), that's why you can't have IHttpContextAccessor injected out of the box. Here are possible solutions. I list them in order of my preference (from best to worst).
If each controller uses only one (or just several) types of data objects, the best options will be to avoid direct instantiation and move toward normal Dependency Injection.
So if ManufacturerController requires only Manufacturer like in your sample then, it's better to inject Manufacturer instance to controller, not to create it inside:
public class Manufacturer1Controller : Controller
{
private readonly Manufacturer manufacturer;
public Manufacturer1Controller(Manufacturer manufacturer)
{
this.manufacturer = manufacturer ?? throw new ArgumentNullException(nameof(manufacturer));
}
[HttpGet]
public async Task<IActionResult> Get()
{
var manufacturers = await manufacturer.SelectMany();
return Ok(manufacturers);
}
// ...
}
IHttpContextAccessor will be injected into Manufacturer and passed to base DbObject:
public class Manufacturer : DbObject<Manufacturer>
{
public Manufacturer(IHttpContextAccessor contextAccessor) : base(contextAccessor)
{
}
}
It's the cleanest solution in the list. You use DI in classic way and utilize all benefits DI provides.
If one controller could use dozens of different data objects, you could inject the factory object that creates instances of data objects. It could be simple implementation based on IServiceProvider:
public interface IDbObjectFactory
{
TDbObject Create<TDbObject>() where TDbObject : DbObject<TDbObject>;
}
public class DbObjectFactory : IDbObjectFactory
{
private readonly IServiceProvider serviceProvider;
public DbObjectFactory(IServiceProvider serviceProvider)
{
this.serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
}
public TDbObject Create<TDbObject>() where TDbObject : DbObject<TDbObject>
{
return serviceProvider.GetRequiredService<TDbObject>();
}
}
public class Manufacturer2Controller : Controller
{
private readonly IDbObjectFactory dbObjectFactory;
public Manufacturer2Controller(IDbObjectFactory dbObjectFactory)
{
this.dbObjectFactory = dbObjectFactory ?? throw new ArgumentNullException(nameof(dbObjectFactory));
}
[HttpGet]
public async Task<IActionResult> Get()
{
var manufacturer = dbObjectFactory.Create<Manufacturer>();
var manufacturers = await manufacturer.SelectMany();
return Ok(manufacturers);
}
}
The code for Manufacturer and DbObject does not change comparing to the first option.
I don't see any reason not to use option #1 or #2. However just to complete the picture, I'll describe another two options.
Inject IHttpContextAccessor into conroller and pass this instance (or IHttpContextAccessor.HttpContext.User) to Data Object constructor invoked with operator new:
public class Manufacturer3Controller : Controller
{
private readonly IHttpContextAccessor contextAccessor;
public Manufacturer3Controller(IHttpContextAccessor contextAccessor)
{
this.contextAccessor = contextAccessor ?? throw new ArgumentNullException(nameof(contextAccessor));
}
[HttpGet]
public async Task<IActionResult> Get()
{
var manufacturer = await new Manufacturer(contextAccessor).SelectMany();
// or
// var manufacturer = await new Manufacturer(contextAccessor.HttpContext.User).SelectMany();
return Ok(manufacturer);
}
}
It's a bad solution, because you don't use Dependency Injection for Manufacturer here and loose many advantages that DI provides.
And the worst option would be using of static object factory with injected IHttpContextAccessor. With this approach you also loose benefits of DI. In addition you get ugly code somewhere in Startup that initializes static instance of IHttpContextAccessor. When you come to this approach, you'll discover that theere is no quite elegant way to do this.
My advice: use option #1 untill you have good reasons against it. Then use option #2.
Here is Sample Project on GitHub with samples for approaches ##1-3.

How to inject HttpHeader value in controller?

I have Web API developed using ASP.NET Core API. Every incoming request has a custom header value inserted. eg x-correlationid. The controller use this value for logging and tracing the request.
Currently I'm reading the value in each controller as below
[Route("api/[controller]")]
public class DocumentController : Controller
{
private ILogger<TransformController> _logger;
private string _correlationid = null;
public DocumentController(ILogger<DocumentController > logger)
{
_logger = logger;
_correlationid = HttpContext.Request.Headers["x-correlationid"];
}
[HttpPost]
public async Task<intTransform([FromBody]RequestWrapper request)
{
_logger.LogInformation("Start task. CorrelationId:{0}", _correlationid);
// do something here
_logger.LogInformation("End task. CorrelationId:{0}", _correlationid);
return result;
}
}
I think this is against DI rules.
Instead of reading the value inside the controller's constructor, I want to inject the value in the controller's constructor.
Or
Can middleware read the x-correlationid and *somehow* make it available to all the controllers so we don't have to inject it in any controller?
What would be a better option here?
Instead of reading the value inside the controller's constructor, I want to inject the value in the controller's constructor.
You can't inject the value itself into the constructor of the api controller, because at the time of construction the HttpContext is going to be null.
One "injection-style" option would be to use the FromHeaderAttribute in your actions:
[HttpPost]
public async Task<int> Transform(
[FromBody]RequestWrapper request,
[FromHeader(Name="x-correlationid")] string correlationId)
{
return result;
}
Can middleware read the x-correlationid and somehow make it available to all the controllers so we don't have to inject it in any controller?
I think a middleware solution would probably be overkill for what you need. Instead, you can create a custom base class that derives from Controller and have all your Api controllers derive from that.
public class MyControllerBase : Controller
{
protected string CorrelationId =>
HttpContext?.Request.Headers["x-correlationid"] ?? string.Empty;
}
[Route("api/[controller]")]
public class DocumentController : MyControllerBase
{
private ILogger<TransformController> _logger;
public DocumentController(ILogger<DocumentController> logger)
{
_logger = logger;
}
[HttpPost]
public async Task<intTransform([FromBody]RequestWrapper request)
{
_logger.LogInformation($"Start task. CorrelationId:{CorrelationId}");
// do something here
_logger.LogInformation($"End task. CorrelationId:{CorrelationId}");
return result;
}
}
This is what I came up with. I think i can also unit test it.
public interface IRequestContext
{
string CorrelationId { get; }
}
public sealed class RequestContextAdapter : IRequestContext
{
private readonly IHttpContextAccessor _accessor;
public RequestContextAdapter(IHttpContextAccessor accessor)
{
this._accessor = accessor;
}
public string CorrelationId
{
get
{
return this._accessor.HttpContext.Request.Headers[Constants.CORRELATIONID_KEY];
}
}
}
then in startup's configureservice method register the adapter
services.AddSingleton<IRequestContext, RequestContextAdapter>();
and inject it in controller
[Route("api/[controller]")]
public class DocumentController : Controller
{
private ILogger<TransformController> _logger;
private IRequestContext _requestContext = null;
public DocumentController(ILogger<DocumentController > logger,IRequestContext requestContext)
{
_logger = logger;
_requestContext = requestContext;
}
[HttpPost]
public async Task<intTransform([FromBody]RequestWrapper request)
{
_logger.LogInformation("Start task. CorrelationId:{0}", _requestContext.CorrelationId);
// do something here
_logger.LogInformation("End task. CorrelationId:{0}", _requestContext.CorrelationId);
return result;
}
}
Depending on your needs one of following is suitable:
If you need your header values at action level, then using FromHeaderAttribute sounds better (lighter and easier).
If you need to use this header value in lower layers like Repository or DAL, which will be instantiated before Controller has been initialized, then consider to use middleware to get header values initialized and available for other components.

ASP.NET 5 (MVC6) How to seed users

How would you seed users? I am following their documents here, but they only show how to seed data that is inserted directly by the ApplicationDbContext.
In the Account controller, the UserManager is created through DI. How would I instantiate a UserManager in the Seed method?
public class SeedData
{
public static void Initialize(IServiceProvider serviceProvider)
{
var context = serviceProvider.GetService<ApplicationDbContext>();
var userManager = serviceProvider.GetService<UserManager<ApplicationUser>>();
Then in Startup.cs in the Configure method:
SeedData.Initialize(app.ApplicationServices);
In the startup.cs in the configure method, you can add the following code to get the UserManager and from there seed the users.
var userManager = app.ApplicationServices.GetService<UserManager<ApplicationUser>>();
You would create a class in the lines of
public class UserSeed
{
private ApplicationDbContext _context;
private UserManager<ApplicationUser> _mgr;
public UserSeed(ApplicationDbContext context,UserManager<ApplicationUser> userManager)
{
_context = context;
_mgr = users;
}
public void UserSeedData()
{
var user = new ApplicationUser { UserName = "foo#foo.com", Email = "foo#foo.com" };
var result = _mgr.CreateAsync(user,"fooPassword");
......
......
}
}
In the configure method of your startup.cs take userSeed through DI like
public async void Configure(...., UserSeed userSeedData)
and then inside the method you call
userSeedData.UserSeedData();
Don't use this code as is, you would probably want to check if the user already exists before you create it.
Note: This method will run once every time your application starts.

Get Injected Object in ASP.NET vNext filter

I'm trying to create my custom authorize attribute, but in asp.net vnext using the default dependency injection framework I don't how to get the injected object. I need to get the injected object in the default ctor.
public class CustomAttribute
{
private IDb _db;
public CustomAttribute()
{
_db = null; // get injected object
}
public CustomAttribute(IDb db)
{
_db = db;
}
// apply all authentication logic
}
You can use the ServiceFilterAttribute for this purpose. The service filter attribute lets the DI system take care of instantiating and maintaining the lifetime of the filter CustomAuthorizeFilter and its any required services.
Example:
// register with DI
services.AddScoped<ApplicationDbContext>();
services.AddTransient<CustomAuthorizeFilter>();
//------------------
public class CustomAuthorizeFilter : IAsyncAuthorizationFilter
{
private readonly ApplicationDbContext _db;
public CustomAuthorizeFilter(ApplicationDbContext db)
{
_db = db;
}
public Task OnAuthorizationAsync(AuthorizationContext context)
{
//do something here
}
}
//------------------
[ServiceFilter(typeof(CustomAuthorizeFilter))]
public class AdminController : Controller
{
// do something here
}