I'm trying to decide if the custom Authorization attribute I wrote is really a good idea.
Scenario
Say we have a collection of stores, each Store has an owner. Only the owner of the store can do CRUD operations on the store. EXCEPT for users with a Claim that basically overrides the ownership requirement and says they can do CRUD operations on ANY store.
Sidenote: I'm using Thinktecture and ADFS
So I made a StoreOwnerAuthorize attribute who's parameters ("Manage", "Stores") are used to check if the user has the appropriate claim to "override" not being an owner but still able to pass the authorization check.
I'm not sure how I feel about having a claim like "ManageStores" and making the database call inside the attribute. It makes me think I'm going down the wrong road, even though it does accomplish exactly what I need.
API Routes
api/v1/Store/{storeId:int:min(1)}/employees
api/v1/Store/{storeId:int:min(1)}/inventory
API Method
[StoreOwnerAuthorize("Manage", "Stores")]
[ResourceAuthorize("View", "Store")]
[Route("")]
//api/v1/Store/{storeId:int:min(1)}/employees
public IHttpActionResult GetStoreEmployees(int storeId)
{
return Ok(collectionOfStoreEmployees);
}
StoreOwnerAuthorizeAttribute
public class StoreOwnerAuthorizeAttribute : ResourceAuthorizeAttribute
{
private readonly DbContext _context = new DbContext();
public StoreOwnerAuthorizeAttribute(){ }
public StoreOwnerAuthorizeAttribute(string action, params string[] resources)
: base(action, resources) { }
protected override bool IsAuthorized(HttpActionContext actionContext)
{
//If the user has the Claim that overrides the requirement that the user
//is the Owner of the Store, skip checking if they are the owner
if (base.IsAuthorized(actionContext))
return true;
//Get route parameter to lookup Store and determine if the user is the owner
object storeId;
actionContext.ControllerContext.RouteData.Values.TryGetValue("storeId", out storeId);
var isOwner = false;
if (storeId != null)
{
isOwner =
_context.Store_Get_ByStoreID(int.Parse(storeId.ToString()))
.Any(x => x.OwnerId == theUser.Id());
}
return isOwner;
}
}
Related
I need advice of where to put custom user authorization code in ASP.NET Core. I am somewhat a ASP.NET Framework developer and normally I will add code to Global.asax as a session_onstart event to look up a SQL table where users profile are stored that is used to determine what they can view in the rest of the application. With Global.asax this is only cause once per user session, so what I would like to do is the same kind of approach in ASP.NET Core which I am kind of new to but need advice where that check should be done
I would like to do is the same kind of approach in ASP.NET Core which
I am kind of new to but need advice where that check should be done
Well, based on your description, in asp.net core you can achieve that in many ways. For instances, you could set in following places:
program.cs/startup.cs files
Using Middleware file
Using Action Filter
Let's, consider below example using action filter
Role and permissison:
First we are defining the role and the permission.
public enum Role
{
User,
Admin,
SuperAdmin
}
public enum Permission
{
Read,
Create,
Update,
Delete
}
Authorization On Action Filter:
public class AuthorizeActionFilter : IAuthorizationFilter
{
private readonly Role _role;
private readonly Permission _permission;
public AuthorizeActionFilter(Role item, Permission action)
{
_role = item;
_permission = action;
}
public void OnAuthorization(AuthorizationFilterContext context)
{
var isAuthorized = context.HttpContext.User.Claims.Any(c => c.Type == _role.ToString() && c.Value == _permission.ToString());
if (!isAuthorized)
{
context.Result = new ForbidResult();
}
}
}
Note: Check your user claim from the HttpContext if that containts either Admin or Read authorization.
Controller:
[Authorize(Role.User, Permission.Read)]
public IActionResult MemberList()
{
var memberList = _context.Members.ToList();
return View(memberList);
}
Output:
You even can implement that using Middleware. Asp.net 6 now providing couple of other mechanism now a days, you could have a look below official implementations as well.
Role-based authorization
Claims-based authorization
Policy-based authorization
Custom Action Filter
I'm trying to implement a web application using ASP.NET MVC and the Microsoft Unity DI framework. The application needs to support multiple user sessions at the same time, each of them with their own connection to a separate database (but all users using the same DbContext; the database schemas are identical, it's just the data that is different).
Upon a user's log-in, I register the necessary type mappings to the application's Unity container, using a session-based lifetime manager that I found in another question here.
My container is initialized like this:
// Global.asax.cs
public static UnityContainer CurrentUnityContainer { get; set; }
protected void Application_Start()
{
// ...other code...
CurrentUnityContainer = UnityConfig.Initialize();
// misc services - nothing data access related, apart from the fact that they all depend on IRepository<ClientContext>
UnityConfig.RegisterComponents(CurrentUnityContainer);
}
// UnityConfig.cs
public static UnityContainer Initialize()
{
UnityContainer container = new UnityContainer();
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
GlobalConfiguration.Configuration.DependencyResolver = new Unity.WebApi.UnityDependencyResolver(container);
return container;
}
This is the code that's called upon logging in:
// UserController.cs
UnityConfig.RegisterUserDataAccess(MvcApplication.CurrentUnityContainer, UserData.Get(model.AzureUID).CurrentDatabase);
// UnityConfig.cs
public static void RegisterUserDataAccess(IUnityContainer container, string databaseName)
{
container.AddExtension(new DataAccessDependencies(databaseName));
}
// DataAccessDependencies.cs
public class DataAccessDependencies : UnityContainerExtension
{
private readonly string _databaseName;
public DataAccessDependencies(string databaseName)
{
_databaseName = databaseName;
}
protected override void Initialize()
{
IConfigurationBuilder configurationBuilder = Container.Resolve<IConfigurationBuilder>();
Container.RegisterType<ClientContext>(new SessionLifetimeManager(), new InjectionConstructor(configurationBuilder.GetConnectionString(_databaseName)));
Container.RegisterType<IRepository<ClientContext>, RepositoryService<ClientContext>>(new SessionLifetimeManager());
}
}
// SessionLifetimeManager.cs
public class SessionLifetimeManager : LifetimeManager
{
private readonly string _key = Guid.NewGuid().ToString();
public override void RemoveValue(ILifetimeContainer container = null)
{
HttpContext.Current.Session.Remove(_key);
}
public override void SetValue(object newValue, ILifetimeContainer container = null)
{
HttpContext.Current.Session[_key] = newValue;
}
public override object GetValue(ILifetimeContainer container = null)
{
return HttpContext.Current.Session[_key];
}
protected override LifetimeManager OnCreateLifetimeManager()
{
return new SessionLifetimeManager();
}
}
This works fine as long as only one user is logged in at a time. The data is fetched properly, the dashboards work as expected, and everything's just peachy keen.
Then, as soon as a second user logs in, disaster strikes.
The last user to have prompted a call to RegisterUserDataAccess seems to always have "priority"; their data is displayed on the dashboard, and nothing else. Whether this is initiated by a log-in, or through a database access selection in my web application that calls the same method to re-route the user's connection to another database they have permission to access, the last one to draw always imposes their data on all other users of the web application. If I understand correctly, this is a problem the SessionLifetimeManager was supposed to solve - unfortunately, I really can't seem to get it to work.
I sincerely doubt that a simple and common use-case like this - multiple users logged into an MVC application who each are supposed to access their own, separate data - is beyond the abilities of Unity, so obviously, I must be doing something very wrong here. Having spent most of my day searching through depths of the internet I wasn't even sure truly existed, I must, unfortunately, now realize that I am at a total and utter loss here.
Has anyone dealt with this issue before? Has anyone dealt with this use-case before, and if yes, can anyone tell me how to change my approach to make this a little less headache-inducing? I am utterly desperate at this point and am considering rewriting my entire data access methodology just to make it work - not the healthiest mindset for clean and maintainable code.
Many thanks.
the issue seems to originate from your registration call, when registering the same type multiple times with unity, the last registration call wins, in this case, that will be data access object for whoever user logs-in last. Unity will take that as the default registration, and will create instances that have the connection to that user's database.
The SessionLifetimeManager is there to make sure you get only one instance of the objects you resolve under one session.
One option to solve this is to use named registration syntax to register the data-access types under a key that maps to the logged-in user (could be the database name), and on the resolve side, retrieve this user key, and use it resolve the corresponding data access implementation for the user
Thank you, Mohammed. Your answer has put me on the right track - I ended up finally solving this using a RepositoryFactory which is instantiated in an InjectionFactory during registration and returns a repository that always wraps around a ClientContext pointing to the currently logged on user's currently selected database.
// DataAccessDependencies.cs
protected override void Initialize()
{
IConfigurationBuilder configurationBuilder = Container.Resolve<IConfigurationBuilder>();
Container.RegisterType<IRepository<ClientContext>>(new InjectionFactory(c => {
ClientRepositoryFactory repositoryFactory = new ClientRepositoryFactory(configurationBuilder);
return repositoryFactory.GetRepository();
}));
}
// ClientRepositoryFactory.cs
public class ClientRepositoryFactory : IRepositoryFactory<RepositoryService<ClientContext>>
{
private readonly IConfigurationBuilder _configurationBuilder;
public ClientRepositoryFactory(IConfigurationBuilder configurationBuilder)
{
_configurationBuilder = configurationBuilder;
}
public RepositoryService<ClientContext> GetRepository()
{
var connectionString = _configurationBuilder.GetConnectionString(UserData.Current.CurrentPermission);
ClientContext ctx = new ClientContext(connectionString);
RepositoryService<ClientContext> repository = new RepositoryService<ClientContext>(ctx);
return repository;
}
}
// UserData.cs (multiton-singleton-hybrid)
public static UserData Current
{
get
{
var currentAADUID = (string)(HttpContext.Current.Session["currentAADUID"]);
return Get(currentAADUID);
}
}
public static UserData Get(string AADUID)
{
UserData instance;
lock(_instances)
{
if(!_instances.TryGetValue(AADUID, out instance))
{
throw new UserDataNotInitializedException();
}
}
return instance;
}
public static UserData Current
{
get
{
var currentAADUID = (string)(HttpContext.Current.Session["currentAADUID"]);
return Get(currentAADUID);
}
}
public static UserData Get(string AADUID)
{
UserData instance;
lock(_instances)
{
if(!_instances.TryGetValue(AADUID, out instance))
{
throw new UserDataNotInitializedException();
}
}
return instance;
}
I'm looking into asp.net core and the new security policies and claims functionality. Having just looked at it I don't see how it is much better than the existing authorize attribute logic in the past where hard-coded roles or users are decorated on controllers, methods etc. To me the issues has just been moved from hard-coding in attributes to hard-coding policies.
Ideally I would like to perform activity/resource based authorization where everything would be database driven. Each activity or resource would be stored in the database and a permission/role would be assigned to the resource.
While researching the topic I found this fantastic article by Stefan Wloch that pretty much covers exactly what I'm looking to do.
http://www.codeproject.com/Articles/1079552/Custom-Roles-Based-Access-Control-RBAC-in-ASP-NE
So my question is with the new core features how does it prevent us from having to hard-code and recompile when the time comes to change what roles/permissions are allowed to access a controller or method in a controller? I understand how claims can be used to store anything but the policy portion seems susceptible to change, which gets us back to square one. Don't get me wrong, loving asp.net core and all the great changes, just looking for more information on how to handle authorization.
There are at least 2 things that need to be consider in implementing what you want. The first one is how to model the Controller-Action access in database, the second one is to apply that setting in asp.net core Identity.
The first one, there are too many possibilities depend on the application itself, so lets create a Service interface named IActivityAccessService that encapsulate. We use that service via dependency injection so that anything that we need can be injected to it.
As for the second one, it can be achieved by customize AuthorizationHandler in a policy-based authorization. The first step is to setup things in Startup.ConfigureServices :
services.AddAuthorization(options =>
{
options.AddPolicy("ActivityAccess", policy => policy.Requirements.Add( new ActivityAccessRequirement() ));
});
services.AddScoped<IAuthorizationHandler, ActivityAccessHandler>();
//inject the service also
services.AddScoped<IActivityAccessService, ActivityAccessService>();
//code below will be explained later
services.AddHttpContextAccessor();
next we create the ActivityAccessHandler:
public class ActivityAccessHandler : AuthorizationHandler<ActivityAccessRequirement>
{
readonly IActivityAccessService _ActivityAccessService;
public ActivityAccessHandler (IActivityAccessService r)
{
_ActivityAccessService = r;
}
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext authHandlerContext, ActivityAccessRequirement 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 (_ActivityAccessService.IsAuthorize(area, controller, action, id))
{
context.Succeed(requirement);
}
}
}
}
public class ActivityAccessRequirement : IAuthorizationRequirement
{
//since we handle the authorization in our service, we can leave this empty
}
Since we can use dependency injection in AuthorizationHandler, it is here that we inject the IActivityAccessService.
Now that we have access to what resource is being requested, we need to know who is requesting it. This can be done by injecting IHttpContextAccessor. Thus services.AddHttpContextAccessor() is added in code above, it is for this reason.
And for the IActivityAccessService, you could do something like:
public class ActivityAccessService : IActivityAccessService
{
readonly AppDbContext _context;
readonly IConfiguration _config;
readonly IHttpContextAccessor _accessor;
readonly UserManager<AppUser> _userManager;
public class ActivityAccessService(AppDbContext d, IConfiguration c, IHttpContextAccessor a, UserManager<AppUser> u)
{
_context = d;
_config = c;
_accessor = a;
_userManager = u;
}
public bool IsAuthorize(string area, string controller, string action, string id)
{
//get the user object from the ClaimPrincipals
var appUser = await _userManager.GetUserAsync(_accessor.HttpContext.User);
//get user roles if necessary
var userRoles = await _userManager.GetRolesAsync(appUser);
// all of needed data are available now, do the logic of authorization
return result;
}
}
Please note that the code in IsAuthorize body above is an example. While it will works, people might say it's not a good practice. But since IActivityAccessService is just a common simple service class, we can inject anything that wee need to it and modify the IsAuthorize method signature in any way that we want to. For example, we can just pass the filterContext.RouteData instead.
As for how to apply this to a controller or action:
[Authorize(Policy = "ActivityAccess")]
public ActionResult<IActionResult> GetResource(int resourceId)
{
return Resource;
}
hope this helps
I'm new to MVC (5). In order to add localization support to my website I added a "Language" field to my ApplicationUser : IdentityUser
What's the best approach to now store this information in the browser and ensure that it gets re-created even if the user manually deletes it?
TL; but I've got time
What I've tried until now:
I started creating a cookie in my method private async Task SignInAsync(ApplicationUser user, bool isPersistent) but I notice that:
This method is not used if the user is already authenticated and automatically logs in using the .Aspnet.Applicationcookie and my language cookie could be meanwhile expired (or been deleted).
A user could manually delete the cookie, just for fun.
I thought about checking its existence in the controller (querying the logged user and getting it from the db) and it works but I'd need to do it in EVERY controller. I'm not sure is the correct way to do this.
Any suggestion about how to approach this problem and guarantee that the application has a valid "language cookie" on every request?
It sounds to me like what you want here is a Custom Action Filter. You can override the OnActionExecuting method which means the logic is run before any action is called
public class EnsureLanguagePreferenceAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var langCookie = filterContext.HttpContext.Request.Cookies["LanguagePref"];
if (langCookie == null)
{
// cookie doesn't exist, either pull preferred lang from user profile
// or just setup a cookie with the default language
langCookie = new HttpCookie("LanguagePref", "en-gb");
filterContext.HttpContext.Request.Cookies.Add(langCookie);
}
// do something with langCookie
base.OnActionExecuting(filterContext);
}
}
Then register your attribute globally so it just becomes the default behaviour on every controller action
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
filters.Add(new EnsureLanguagePreferenceAttribute());
}
To me, the easiest way would be to create your own Authorize attribute (since your language options are tied to an authenticated user account). Inside of your new authorize attribute, simply perform the check if the cookie exists. If it does, then life is good. Else, query the user's database profile and reissue the cookie with the stored value
public class MyAuthorization : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
//no point in cookie checking if they are not authorized
if(!base.AuthorizeCore(httpContext)) return false;
var cookie = httpContext.Request.Cookies["LanguageCookie"];
if (cookie == null) {
CreateNewCookieMethod();
}
return true;
}
}
To use, replace [Authorize] with [MyAuthorization] in your project.
If you don't want to mess with the [Authorize] attribute, you could create your own attribute that does the cookie checking and decorate your controller with that one as well.
One last alternative is to create your own Controller class that does the checking on the OnActionExecuting.
public class MyBaseController : Controller
{
public string Language {get;set;}
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
var cookie = filterContext.HttpContext.Request.Cookies["LanguageCookie"];
if(cookie == null){
cookie = CreateNewCookieMethod();
filterContext.HttpContext.Request.Cookies.Add(cookie);
}
Language = cookie.Value;
base.OnActionExecuting(filterContext);
}
How to use (note that we inherit from MybaseController now)
public class HomeController : MyBaseController{
public ActionResult Index(){
//Language comes from the base controller class
ViewBag.Language = Language;
Return View();
}
}
This method is neat because now that Language variable will be available in any controller that inherits from this new class.
Either of these will give you a single, cookie checking point. Additionally, you are only going back to the database only in the instance that the cookie does not exist.
I have the following:-
I am working on an asset management system using Asp.net MVC4 with windows authentication enabled.
The system allow to specify what actions a group of users can do(for example certain group can have the authority to add new physical asset , while they can only read certain logical asset, and so on).
So I found that using the build-in Asp.net role management, will not allow me to have the level of flexibility I want. So I decided to do the following:-
I have created a table named “group” representing the user groups. Where users are stored in active directory.
I have created a table named ”Security Role” which indicate what are the permission levels each group have on each asset type(edit, add, delete or view)per asset type.
Then on each action methods , I will use Helper methods to implement and check if certain users are within the related group that have the required permission ,, something such as
On the Car model object I will create a new helper method
Public bool HaveReadPermison(string userName) {
//check if this user is within a group than have Read permission on CARS, //OR is within a GROUP THAT HAVE HIGHER PERMISON SUCH AS EDIT OR ADD OR //DELETE.
}
Next, On the Action method, I will check if the user has the Read permission or not by calling the action method:-
public ActionResult ViewDetails(int id) { // to view transportation asset type
Car car = repository.GetCar(id);
if (!car.HaveReadPermision(User.Identity.Name)) {
if (car == null)
return View("NotFound");
else
return View(car);
}
else
return view (“Not Authorized”);
So can anyone advice if my approach will be valid or it will cause problem I am unaware about.
Regards
In my opinion, once you have decided to use the ASP membership and role providers you can keep leveraging them also for authorization, simply using the Authorize attribute. This will also allow to restrict access by user names and roles.
What the attribute won't do is Action-based authorization. In that case there are a few options but in my opinion this could be brilliantly resolved by a Custom Action Filter based loosely on the following code:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class CheckUserPermissionsAttribute : ActionFilterAttribute
{
public string Model { get; set; }
public string Action { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var user = filterContext.HttpContext.User.Identity.Name; // or get from DB
if (!Can(user, Action, Model)) // implement this method based on your tables and logic
{
filterContext.Result = new HttpUnauthorizedResult("You cannot access this page");
}
base.OnActionExecuting(filterContext);
}
}
Yes, it is vaguely inspired to CanCan, which is a nice Ruby gem for this kind of things.
Returning Unauthorized (401) will also instruct your server to redirect to the login page if one is specified. You may want to work on that logic if you want to redirect somewhere else. In that case you should do:
filterContext.Result = new RedirectToRouteResult(new System.Web.Routing.RouteValueDictionary { { "Controller", "Home" }, { "Action", "Index" } });
and choose the appropriate controller/action pair.
You can use the attribute like this:
[CheckUserPermissions(Action = "edit", Model = "car")]
public ActionResult Edit(int id = 0)
{
//..
}
Let me know if that works nicely for you.
The approach you took looks reasonable, but I would add few changes:
What if you forgot to call HaveReadPermision method? And checking authotization from Actions is not the cleanest solution either, that is not an Action reponsibility.
It is better to keep authorization logic separately. For instance you can create a decorator over you repository which will check the permissions of the current User:
public class AuthorizationDecorator: IRepository
{
public AuthorizationDecorator(IRepository realRepository, IUserProvider userProvider)
{
this.realRepository = realRepository;
this.userProvider = userProvider;
}
public Car GetCar(int id)
{
if(this.UserHaveReadPermission(this.userProvider.GetUserName(), Id))
{
return this.realRepository.GetCar(id);
}
else
{
throw new UserIsNotAuthorizedException();
}
}
private bool UserHaveReadPermission(string username, int id)
{
//do your authorization logic here
}
}
IUserProvider will return curent user name from httpRequest.
After doing the change you don't need to warry about authorization when writing Actions