I am using asp.net core mvc with asp.net identity.
My ApplicationUser has a property: Country => 'GB' or 'FR' etc...
I want to set the System.Threading.Thread.CurrentThread.CurrentCulture to the value read from the applicationUser.Country. to display all datetime/number values correctly in my views as set by the user.
Thus I created this action filter:
public class LanguageActionFilter : ActionFilterAttribute
{
private readonly ILogger _logger;
private UserManager<ApplicationUser> _userManager;
public LanguageActionFilter(ILoggerFactory loggerFactory, UserManager<ApplicationUser> userManager)
{
_logger = loggerFactory.CreateLogger("LanguageActionFilter");
_userManager = userManager;
}
public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
var user = await _userManager.GetUserAsync(context.HttpContext.User);
Thread.CurrentThread.CurrentCulture = new CultureInfo(user.Country);
await base.OnActionExecutionAsync(context, next);
}
}
On the action/controller where this actionfilter is set I also run this code:
public async Task<IActionResult> Index()
{
var user = await this.userManager.GetUserAsync(User);
var todos = await service.GetTodosAsync(user.Id);
return View(todos);
}
At the moment I call the this.userManager.GetUserAsync(User);
TWO TIMES !
Then I thought about passing the user object to the
context.HttpContext.Items.Add("applicationUser", user);
and grab this user object again within the Index() action method:
var user = (ApplicationUser)base.HttpContext.Items["applicationUser"];
That worked, but is that a good idea? Do you see any problems with that approach?
What you did is fine...
I'm quoting from the definition of the HttpContext class :
/// <summary>
/// **Gets or sets a key/value collection that can be used to share data within the scope of this request.**
/// </summary>
public abstract IDictionary<object, object> Items { get; set; }
Edit : It seems ef core doesn't support first level cache. So my idea went for nothing
Your code works, but i think you won't gain considerable performance.
You probably use aspnet identity with entity framework. Since ef supports first level cache. , if you call this.userManager.GetUserAsync(User); more than one, ef retrieves user entity from database only once. As a result two ways approximately have same effect for performance. I wouldn't use HttpContext.Items for your case.
Related
I have a .Net Core application with EF which returns a page called Customer as home page. But I need show an app not available page (AppNotAvialable.cshmtl) when the user tries to access the application on a holiday(holiday list is stored in a table)instead of the Customer page. Instead of querying the holiday list table every time the home page is accessed I am trying to use the In-Memory Cache so I can store the query response in cache and use them in the controller. I am implementing the cache for the 1st time and below is what I tried
public class CustomersController : Controller
{
private readonly SurplusMouseContext _context;
private readonly IMemoryCache memoryCache;
.......................
public async Task<IActionResult> Index(string sortOrder, string searchString,
int? pageNumber, string currentFilter)
{
int holidaycheck;
var timeUtc = DateTime.UtcNow;
var easternZone = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
var todayDt = TimeZoneInfo.ConvertTimeFromUtc(timeUtc, easternZone);
bool isExist = memoryCache.TryGetValue("HolidayWk", out holidaycheck);
if (!isExist)
{
holidaycheck = (from hc in _context.HolidayWeeks
where hc.HolidateDate.Date == todayDt.Date
select hc).Count();
var cacheEntryOptions = new MemoryCacheEntryOptions()
.SetSlidingExpiration(TimeSpan.FromHours(2));
memoryCache.Set("HolidayWk", holidaycheck, cacheEntryOptions);
}
if (holidaycheck != 0)
{
return View("/Views/Customers/AppNotAvailable.cshtml");
}
else
{
when I try to debug the application it throws error like below
Can anyone please suggest if I am implementing the in Memory Cache here correct/missing anything and where should I be doing the cacheMemory.Remove() Any help is greatly appreciated
The first step to check:
In your question, it is possible you didn't initialize MemoryCache object in the constructor.
There is an example of MemoryCache.
1 - You should register MemoryCache in services like this:
services.AddMemoryCache();
2 - Inject IMemoryCache interface in your controller and initialize it.
public class YourController : Controller
{
private readonly IMemoryCache _memoryCache;
public YourController(IMemoryCache memoryCache)
{
_memoryCache = memoryCache
}
}
3 - Using memory cache:
public async Task<IActionResult> Index(string sortOrder, string searchString,
int? pageNumber, string currentFilter)
{
bool isExist = _memoryCache.TryGetValue("HolidayWk", out holidaycheck);
}
I have a blazor server web application and a .NET Core worker process, these both use a common class for data access (generic unit of work / generic repository).
In the database I would like to log the user names that are inserting or editing records. To do this I want to inject a ClaimsPrincipal to the shared UoW and Repo classes).
So, I would like to be able to extract the current ClaimsPrincipal in a transient service via dependency injection.
For the worker I can inject a ClaimsPrincipal via the following code;
public static IServiceCollection CreateWorkerClaimsPrincipal(this IServiceCollection services, string workerName)
{
Claim workerNameClaim = new Claim("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", workerName);
ClaimsIdentity identity = new ClaimsIdentity(
new System.Security.Claims.Claim[] { workerNameClaim },
"My-Worker-Authentication-Type",
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name",
"role");
ClaimsPrincipal principal = new ClaimsPrincipal(identity);
services.AddTransient<ClaimsPrincipal>(s => principal);
return services;
}
This is working and meets my needs.
For the blazor server web application I need to do something similar.
I believe that the correct way to extract the ClaimsPrincipal is via the AuthenticationStateProvider, however this needs a call to an async method GetAuthenticationStateAsync.
NOTE: I cannot user IHttpContextAccessor as this doesn't work with Azure App Service.
I want something like;
public void ConfigureServices(IServiceCollection services)
{
/// ...
services.AddTransient<ClaimsPrincipal>(); // I think I need to do something here?
/// ...
}
So when I request a ClaimsPrincipal via dependency injection I want to return the user from;
var authState = await AUthenticationStateProvider.GetAuthenticationStateAsync();
return authState.User;
Is this possible?
As is often the way, by working this through into a simple example for a SO post I have found a workable (I think) solution from https://learn.microsoft.com/en-us/aspnet/core/blazor/security/?view=aspnetcore-5.0#implement-a-custom-authenticationstateprovider
NOTE: I'm still not 100% sure if the async init pattern will always resolve the AuthenticationState before the Repository property is called, but its hanging together so far... Just beware of this if you choose to use this code.
I have changed the approach, and instead of trying to resolve ClaimsPrincipal via DI (because AuthenticationStateProvider is not available for a worker process), I have created a custom AuthenticationStateProvider in the worker.
public class WorkerAuthStateProvider : AuthenticationStateProvider
{
private readonly string _workerName;
public WorkerAuthStateProvider(string workerName)
{
_workerName = workerName;
}
public override Task<AuthenticationState> GetAuthenticationStateAsync()
{
var identity = new ClaimsIdentity(new[] {
new Claim(ClaimTypes.Name, _workerName),
}, "My-Worker-Authentication-Type");
ClaimsPrincipal user = new ClaimsPrincipal(identity);
return Task.FromResult(new AuthenticationState(user));
}
}
and then register this in configureServices to resolve for instances of AuthenticationStateProvider in the worker program.cs file (also passing a custom worker process name, so I can use this on all my workers);
services.AddScoped<AuthenticationStateProvider, WorkerAuthStateProvider>(serviceProvider =>
{
return new WorkerAuthStateProvider(Constants.Logging.RoleNames.MYWORKERNAME);
});
The AuthenticationStateProvider already works in the blazor web apps so this allows me to resolve this correctly, in the constructor for my GenericUnitOfWork pattern for data access on both Web and Workers, for example;
private TDbContext _dbContext;
private readonly ILogger<TEntity> _logger;
private GenericRepository<TEntity, TDbContext> _repository;
private ClaimsPrincipal _user;
private readonly AuthenticationStateProvider _authenticationStateProvider;
public GenericUnitOfWork(TDbContext context, ILogger<TEntity> logger, AuthenticationStateProvider authenticationStateProvider)
{
_dbContext = context;
_logger = logger;
_authenticationStateProvider = authenticationStateProvider;
UserInit = InitUserAsync();
}
/// <summary>
/// Async initialisation pattern from https://blog.stephencleary.com/2013/01/async-oop-2-constructors.html
/// </summary>
public Task UserInit { get; private set; }
private async Task InitUserAsync()
{
var authState = await _authenticationStateProvider.GetAuthenticationStateAsync();
_user = authState.User;
}
public IGenericRepository<TEntity, TDbContext> Repository
{
get
{
if (_repository == null)
{
// when accessing the repository, we are expecting to pass the current application claims principal
// however the ClaimsPrincipal is resolved using an Async method from the AuthenticationStateProvider.
// In the event that the Async method has not yet completed we need to throw an exception so we can determine
// if a further async code fix is required.
if (_user == null)
{
throw new InvalidOperationException("Async ClaimsPrincipal has not been loaded from the AuthenticationStateProvider");
}
_repository = new GenericRepository<TEntity, TDbContext>(_dbContext, _logger, _user);
}
return _repository;
}
}
I am struggling to find a good solution for doing custom authorization checks without having to repeat the authorization check manually over and over again.
To illustrate, suppose I have the following setup for a .net core web api, which has two endpoints, one for GET and one for POST. I would like to check (maybe against db) whether the user has the right to see the resource, or the right to create a resource.
This is what the documentation refers to as resource based authorization
and would look something like this:
[Authorize]
[ApiVersion ("1.0")]
[Route ("api/v{version:apiVersion}/[controller]")]
[ApiController]
public class ResourcesController : ControllerBase {
private readonly IAuthorizationService _authorizationService;
//..constructor DI
[HttpGet ("{resourceId}")]
public ActionResult<Resource> Get (Guid resourceId) {
var authorizationCheck = await _authorizationService.AuthorizeAsync (User, resourceId, ServiceOperations.Read);
if (!authorizationCheck.Succeeded) {
return Forbid ();
}
return Ok (ResourceRep.Get (resourceId));
}
[HttpPost]
public ActionResult<Resource> Post ([FromBody] Resource resource) {
var authorizationCheck = await _authorizationService.AuthorizeAsync (User, null, ServiceOperations.Write);
if (!authorizationCheck.Succeeded) {
return Forbid ();
}
return Ok (ResourceRep.Create (resource));
}
}
Now imagine the ServiceOperations enum has a long list of supported operations, or there are 100 different endpoints, I will have to do the same check everywhere, or even worse, might forget to add a check where I should definitely have added a check. And there is not an easy way to pick this up in unit tests.
I thought of using attributes but as the docs state:
Attribute evaluation occurs before data binding and before execution of the page handler or action that loads the document. For these reasons, declarative authorization with an [Authorize] attribute doesn't suffice. Instead, you can invoke a custom authorization method—a style known as imperative authorization.
So it seems I cannot use an authorization policy and decorate the methods with authorization attributes (which are easy to unit test that they are there) when the check itself requires a parameter that is not available (the resourceId).
So for the question itself:
How do you use imperative (resource based) authorization generically without having to repeat yourself (which is error-prone). I would love to have an attribute like the following:
[HttpGet ("{resourceId}")]
[AuthorizeOperation(Operation = ServiceOperations.Read, Resource=resourceId)]
public ActionResult<Resource> Get (Guid resourceId) {..}
[AuthorizeOperation(Operation = ServiceOperations.Write)]
[HttpPost]
public ActionResult<Resource> Post ([FromBody] Resource resource) {..}
You can achieve it using AuthorizationHandler in a policy-based authorization and combine with an injected service specifically created to determine the Operation-Resources pairing.
To do it, first setup the policy in Startup.ConfigureServices :
services.AddAuthorization(options =>
{
options.AddPolicy("OperationResource", policy => policy.Requirements.Add( new OperationResourceRequirement() ));
});
services.AddScoped<IAuthorizationHandler, UserResourceHandler>();
services.AddScoped<IOperationResourceService, OperationResourceService>();
next create the OperationResourceHandler :
public class OperationResourceHandler: AuthorizationHandler<OperationResourceRequirement>
{
readonly IOperationResourceService _operationResourceService;
public OperationResourceHandler(IOperationResourceService o)
{
_operationResourceService = o;
}
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext authHandlerContext, OperationResourceRequirement requirement)
{
if (context.Resource is AuthorizationFilterContext filterContext)
{
var area = (filterContext.RouteData.Values["area"] as string)?.ToLower();
var controller = (filterContext.RouteData.Values["controller"] as string)?.ToLower();
var action = (filterContext.RouteData.Values["action"] as string)?.ToLower();
var id = (filterContext.RouteData.Values["id"] as string)?.ToLower();
if (_operationResourceService.IsAuthorize(area, controller, action, id))
{
context.Succeed(requirement);
}
}
}
}
the OperationResourceRequirement can be an empty class:
public class OperationResourceRequirement : IAuthorizationRequirement { }
The trick is, rather than specify action's Operation in attribute, we specify it elsewhere such as in database, in appsettings.json, in some config file, or hardcoded.
Here's an example getting the Operation-Resource pair from config file:
public class OperationResourceService : IOperationResourceService
{
readonly IConfiguration _config;
readonly IHttpContextAccessor _accessor;
readonly UserManager<AppUser> _userManager;
public class OpeartionResourceService(IConfiguration c, IHttpContextAccessor a, UserManager<AppUser> u)
{
_config = c;
_accessor = a;
_userManager = u;
}
public bool IsAuthorize(string area, string controller, string action, string id)
{
var operationConfig = _config.GetValue<string>($"OperationSetting:{area}:{controller}:{action}"); //assuming we have the setting in appsettings.json
var appUser = await _userManager.GetUserAsync(_accessor.HttpContext.User);
//all of needed data are available now, do the logic of authorization
return result;
}
}
Please note that to make IHttpContextAccessor injectable, add services.AddHttpContextAccessor() in Startup.ConfigurationServices method body.
After all is done, use the policy on an action:
[HttpGet ("{resourceId}")]
[Authorize(Policy = "OperationResource")]
public ActionResult<Resource> Get (Guid resourceId) {..}
the authorize policy can be the same for every action.
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.
I have this seeder class which is called at the end of the Startup.cs file in the Configure method:
public class UserSeeder
{
private readonly ApplicationDbContext _context;
private readonly UserManager<ApplicationUser> _userManager;
public UserSeeder(ApplicationDbContext context, UserManager<ApplicationUser> userManager)
{
_context = context;
_userManager = userManager;
}
public async Task Seed()
{
if (!await _context.Users.AnyAsync())
{
var user = new ApplicationUser()
{
UserName = "admin",
Email = "admin#test.com"
};
await _userManager.CreateAsync(user, "passwort4admin");
}
}
}
The code is executed and I even put a try/catch around the method call but no error happens AND no user is inserted into the database!
Why not?
The problem is the complexity of the password. Add capital and numbers and symbols the issue will solve
Behind the scenes the UserManager<> uses a IUserStore did you configure this userstore in the startup.cs in the IOC container?
services.AddIdentity<ApplicationUser, ApplicationRole>()
.AddEntityFrameworkStores<MyContext, Guid>()
.AddUserStore<ApplicationUserStore>() //this one provides data storage for user.
.AddRoleStore<ApplicationRoleStore>()
.AddUserManager<ApplicationUserManager>()
.AddRoleManager<ApplicationRoleManager>()
.AddDefaultTokenProviders();
In my case, it was a space in the user name. It's possible that you have other constraints set up that make the user creation operation illegal.
In most cases, you can obtain explicit information on the exact cause of the error by investigating the message in the returned object from the operation.
IdentityUser userToBe = ...
IdentityResult result = await UserManager.CreateAsync(userToBe);
if(!result.Succeeded)
foreach(IdentityError error in result.Errors)
Console.WriteLine($"Oops! {error.Description} ({error.Code}));
I forgot to set "Username", it cannot be empty.
I set UserName=userEmail and it works