I have been finding some solution to put security on basis of entity.Like a user can only access to entity to which it has access.
Rather than putting check on each and every action method can i control in centralized way. I am here talking about access entity using ajax call also. For example a user has opened a orderId 10 for that i have an hidden field if by any means if he changes the value of orderId to 11 he can access or modify order with orderId 11 while he was allowed to see only orderId 10 .
There are the time we just send some values along with main entity id for example getOrderByUserId(int userId) as this action method is in OrderController accessing order based on userId.
Please take look at AuthorizeAttribute and the roles in specific
usage :
[Authorize(Roles = "manager,admin")]
public ActionResult myMethod(){
// your code
}
And you can use the Users property and do something like this:
[Authorize(Users = UsersHelper.GetAllowedUsers)]
public ActionResult myMethod(){
// your code
}
where UsersHelper.GetAllowedUsers is a static class with a static method that returns users in format like this : "joe1,admin,momandpop"
update to OP comment :
/// <summary>
/// Checks if the current user is the owner of the Order
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class IsOwnerOfOrderAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
if (!(filterContext.Result is HttpUnauthorizedResult))
{
// code to check if user has the order he is trying to edit
// if not return this
filterContext.Result = new HttpUnauthorizedResult();
}
}
}
you can place this on top of a controller or a specific action.
you are not talking about entity based security but row-wise security as provided by mssql.
Entity Based security is if a user is allowed to edit, he will be able to edit any other id.
For this you have to maintain hierarchy of user roles and then storing the minimum role that can perform edit or any action on each row in the table.
Or if you want to block user from using query parameter, you can use parameter or session or TempData to transfer data between actions to transfer id and work.
Related
I have a provider hosted sharepoint add-in which uses a database at back end. This database has some roles like Employer and Employee in DB marked with a number. For example 1 for Employer and 2 for Employee and corresponding to every row a sharepoint email address.
In my add-in, I want to mark all my actions with [Authorize(Role="Employer")] attribute but I am not sure how to proceed? If I create a custom filter then, does that mean on every action, I need to call SP to get current logged in user email address -> query DB using it -> find role -> proceed or give access denied. It will consume lots of time as there is already a SPContextFilter on every action.
I was initially saving current user details in a cookie (HttpOnly set to true) but got to know that anyone can edit it using browser extension and impersonate users.
I am pretty new to MVC so any help is appreciated.
I don't see any other way around, you will have to make a DB call for the first new request and for the subsequent requests save the user and role details in some persistent object. Consider using ViewState objects and maybe check for null before proceeding to make a database call and populating the ViewState again.
Always avoid saving user details in cookie. Check the user access with the db.
Create User Access Table to check whether the user has access or not
Use Action Filter, which will execute before the Action execute.
In controller
[RoleAccess]
public class AccountController : Controller
{
public ActionResult Index()
{
your code
}
}
The [RoleAccess] is a Action filter function.
In FilterConfig.cs
public class RoleAccessAttribute : ActionFilterAttribute
{
private ApplicationDbContext db = new ApplicationDbContext();
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var controllerName = (string)filterContext.RouteData.Values["controller"];
var actionName = (string)filterContext.RouteData.Values["action"];
var userID = HttpContext.Current.User.Identity.GetUserId();
if (access not accepted)
{
//return the user to access denied page
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary {
{"controller","Error"},
{"action","Error403"}
});
}
}
}
If the access is accepted then the user is authorized to access the requested Action
hope this helps
In my MVC 4 site, authenticaiton is configured for intranet.
Now, i want to get logged in UserID,
How to fetch the user ID at some common place when accessing any first action (on particular user session) ?
Also, how to fetch User ID existed in context ?
Currently, i wrote session set logic User.Identity.Name on each controller index action.
Is this possible to move that logic for session set into some common place ?
Sorry for confusion.
Got the solutio. Created Action Filter as below and applied at each controller as attribute so, that code execute each time and i added relatd stuff at this place:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class ControllerLogAndAccessFilter : FilterAttribute, IActionFilter
{
public void OnActionExecuting(ActionExecutingContext filterContext)
{
}
I want to be able to globally access the logged-in user in (Controllers, HtmlHelpers and other helper classes) throughout the whole application without getting exceptions that User is null and with the ability to handle guests (not-logged-in users).
Should this be handled within my UsersRepository? If so, how to? or is there a better approach?
You can create custom identity and principal classes. Your custom identity class can extend whatever you are currently using and can then simply add the extra information you need.
Then in a global action filter, simply overwrite the current principal with your customized one. Then you can access it from anywhere like a normal identity, but if you need your additional information, you simply cast it to your custom identity class. Which will grant you access to your additional information.
You can write a custom action filter that is executed on every request (you register it as a global filter). This filter would load the user (from the user´s repository for example) and put it the http context for example or in the ViewData.
EDIT:
Ok, the code for the filter could look like this (in this case, it loads the user to the ViewData collection). I didn´t consider anonymous users here.
public class LoadUserToViewDataAttribute : ActionFilterAttribute
{
private IUserRepository _userRepository;
public LoadUserToViewDataAttribute(IUserRepository userRepository)
{
_userRepository = userRepository;
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var controller = filterContext.Controller;
var userName = filterContext.HttpContext.User.Identity.Name;
var user = _repository.GetUser(userName);
controller.ViewData.Add("CurrentUser", user);
}
}
I am implementing a collaborative web gallery, and I have a few roles for each user:
Admin
DeleteImage
DeleteOwnImage
etc..
For any controller-action, we can apply [Authorize] tag to them plus which roles we want to allow, right? It is fine for Admin/DeleteImage since these two are global; but my question is, like DeleteOwnImage is kind of contextual, in order to determine whether it is valid, we need:
To know what image it is trying to delete (from request)
Retrieve the owner of that image (from service or repository)
Compare current user = that owner
Obviously [Authorize] is not enough to do so, but is it possible to do that on custom ActionFilters? Any hint?
Yes, this is possible with a custom action filter. You can extend from AuthorizeAttribute, the most basic implementation being something like:
public class OwnImageAuthorizeAttribute : AuthorizeAttribute {
public string ImageIdKey { get; set; }
protected override bool AuthorizeCore(HttpContextBase httpContext) {
bool authorized = false;
// Get the current user
var currentUser = ...;
// Get the image ID, whether it is in the route or querystring
int imageId
if(int.TryParse(httpContext.RouteData.Values(ImageIdKey), out imageId)) {
// From querystring: httpContext.Request.Querystring[ImageIdKey]
// Authorize the user
authorized = YourMethodToCheckIfUserIsOwner(currentUser, imageId);
}
return authorized;
}
Then, decorate your method:
[OwnImageAuthorize(ImageIdKey = "imageId")]
public ActionResult MyAction() { }
You can find some more details here.
You can easily add something like this in an ActionFilter, Just add an action filter with OnActionExecuting implemented.
Although, depending on your DB schema this could be achieved on the DB level with your query. You could just delete when owner equals recieved recieved id. (I mean, inside the action method and not in the filter)
EDIT:
If you're using some kind of IOC container for the repositories, you should look around for the new IOC features in MVC3 (if you're using MVC3) to inject dependencies into your action filters.
http://bradwilson.typepad.com/blog/2010/07/service-location-pt4-filters.html
EDIT2:
BTW, I myself don't really like doing too much business logic in ActionFilters, especially involving calls to the DB. Even more when it's something very specific that'll be used for one action.
I am working on an ASP.NET MVC application. I have implemented custom membership provider, principal and identity. In the custom provider I replace the HttpContext.Current.User in the ValidateUser() method as follows:
public sealed class CustomMembershipProvider : MembershipProvider {
...
public override bool ValidateUser(string username, string password) {
...
CustomIdentity identity = new CustomIdentity(...);
CustomPrincipal cu = new CustomPrincipal(identity);
HttpContext.Current.User = cu;
...
}
...
}
In the AccountController (calls the custom membership provider) I am able to access the custom identity as follows:
public class AccountController : BaseController {
...
public ActionResult LogOn(string userName,
string password,
bool rememberMe,
string returnUrl) {
...
CustomIdentity ci = (CustomIdentity)HttpContext.User.Identity;
...
}
...
}
All my controllers inherit the BaseController which calls a custom attribute as follows:
[CustomAttribute]
public abstract class BaseController : Controller {
...
}
I want my other controllers to access the custom identity within the custom attribute after it has been set by the AccountController as follows:
public class CustomAttribute : ActionFilterAttribute {
public override void OnActionExecuting(ActionExecutingContext filterContext) {
base.OnActionExecuting(filterContext);
CustomIdentity ci = filterContext.HttpContext.User.Identity as CustomIdentity;
...
}
}
}
What I have found is that filterContext.HttpContext.User is still set to GenericPrincipal and not my CustomPrincipal. So my custom identity is not accessible within my attribute filter. What do I have to do so that my CustomPrincipal is accessible within my attribute filter?
Thanks in advance.
After researching more about how application request events are fired (the specific order) and when context objects can be set I was able to set my custom principal and identity so that they are available to the filter (throughout the application for that matter).
I realized that the user must be authenticated before these entities could be set for use through the rest of the application. This, I found, could be done in the Application_AuthenticateRequest() method of global.asax.
So, I modified my logic as follows:
Removed creating custom principal and identity from the custom provider's ValidateUser() method.
Instead, ValidateUser() after verifying username and password against the custom repository, caches whatever information I needed within HttpContext.Current.Cache using the user name as the unique key.
Finally, I added the following logic in Application_AuthenticateRequest() to set set my custom principal and identity by extracting the generic identity properties and extending it with custom properties that I stored in the cache. I indexed into the cache using the name stored within generic identity as that is the key I used to create the cache.
protected void Application_AuthenticateRequest(object sender, EventArgs e) {
if (Request.IsAuthenticated) {
// TODO: Add checks so we only do the following once per login.
// Get the GenericPrincipal identity
IIdentity ui = HttpContext.Current.User.Identity;
/* Extract Name, isAuthenticated, AuthenticationType from
the identity of the GenericPrincipal and add them including
any custom properties to the custom identity. I added a
few extra properties to my custom identity. */
CustomIdentity identity = new CustomIdentity(...);
/* Although my custom principal does not currently
have any additional properties, I created a new
principal as I plan to add them in the future. */
CustomPrincipal principal = new CustomPrincipal(identity);
// Set custom principal
HttpContext.Current.User = principal;
}
}
This got me past my hurdle. Please guide me if there are other better ways to accomplish the same.
Thanks.
I don't know if this is "better" way, but it worked for me so far. I create a static UserContext class that has CurrentUser property. There I store the user entity I get from database and use it for user info data and authorization. I only use the HttpContext.Current.User to check authentication.
Now the CurrentUser property stores my user object in HttpContext's Items colletion (I have a wrapper around that so I can make it unit testable).