I am designing an MVC 3 application where multiple tenants reside in a single database.
What is the best way to prevent users from editing/viewing other tenants data in MVC? (i.e. someone could type in '/People/Edit/1' and edit the person with Id of 1- regardless of wether they are part of the tenants data or not).
I know I can override 'OnActionExecuting(ActionExecutingContext filterContext)' for each controller- but it sounds crazy to have to handle each action seperately, get the ID or OBJECT depending on if its a POST or GET and then check if the operation is allowed.
Any better ideas?
Also, I do not want to go down the route of creating a different database or schema for each tenant.
Thanks in advance.
Instead of passing ids to your controller actions write a custom model binder for your entities which will fetch it from the database. So for example let's assume that you have the following model:
public class Person
{
public string Id { get; set; }
... some other properties
}
Now instead of having:
[HttpPost]
public ActionResult Edit(string id)
{
...
}
write:
[HttpPost]
public ActionResult Edit(Person person)
{
...
}
and then write a custom model binder for Person:
public class PersonModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var id = bindingContext.ValueProvider.GetValue("id");
// check if an id was provided and if the user is authenticated
if (!controllerContext.HttpContext.User.Identity.IsAuthenticated || id == null)
{
throw new HttpException(403, "Forbidden");
}
var currentUser = controllerContext.HttpContext.User.Identity.Name;
// fetch the person from your repository given the id and belonging
// to the currently authenticated user
var person = _repository.GetPerson(id.AttemptedValue, currentUser);
if (person == null)
{
// no person found matching
throw new HttpException(403, "Forbidden");
}
return person;
}
}
which you would register in Application_Start:
ModelBinders.Binders.Add(typeof(Person), new PersonModelBinder());
Original answer
To solve the problem quickly use guids instead of a auto-increasing integer. However this is just delaying the problem.
One of the things you can do is to role your own authorize attribuut http://msdn.microsoft.com/en-us/library/system.web.mvc.authorizeattribute.aspx Or you can chose to create a global actionfilter. http://www.asp.net/mvc/tutorials/understanding-action-filters-cs
Addition information on how you could do it based on request in comment
public class MySuperFilter : ActionFilterAttribute
{
//Called by the MVC framework before the action method executes.
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
String user = filterContext.HttpContext.User.Identity.Name;
int id = int.Parse(filterContext.RouteData.GetRequiredString("Id"));
if (!IsValidUser(user,id))
{
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary {{ "Controller", "YourController" },
{ "Action", "YourAction" } });
}
base.OnActionExecuting(filterContext);
}
private bool IsValidUser(string user,int id)
{
//Check if the user has acces to the page
return true;
}
}
It's a non-elegant solution but depending on scope it could be easiest. Having designed a similar system that has relatively few points of entry for multiple tenants data we just merely check that the CurrentUser is the Owner of the object that is being queried. Our use objects a common base interface that has the owner field so the check is made not carrying about the specific object but just from the interface. If there's a mismatch we throw a security exception and log that a user is probably playing around the query string seeing if they get our website to leak data the way many production websites do.
Related
I was looking for an implementation / example of loading and authorizing a resource at a controller level. I am looking for the same functionality as load_and_authorize_resource in the cancan gem in ruby on rails.
Has anyone come across one / have an example how to implement something similar using Mvc .Net attributes?
Thanks!
The load_and_authorize_resource behaviour
With rails, controller and model names are linked up by convention. The attribute load_and_authorize_resource takes that to its advantage. When an action is hit that requires an instance of a resource, the load_and_authorize_resource verifies whether the instance of the resource can be accessed. If it can, it will load it up in an instance variable, if it cant, it will return a 404 or any error behaviour you have configured the attribute to produce.
For example, if I have a resource picture, and only user that own a certain picture can edit the picture's name.
So we would have a Edit action, which obviously would have a pictureId of the picture you want to edit. load_and_authorize_resource would verify whether the current context/user has access to the resource.
Here is a small video introduction of the module.
I am not aware of the existence of such plugin for ASP.NET MVC. To mimic it's functionality you could write a custom Authorize attribute though:
public class LoadAndAuthorizeResourceAttribute : AuthorizeAttribute
{
private class ModelDescriptor
{
public string Name { get; set; }
public Type ModelType { get; set; }
}
private const string ModelTypeKey = "__ModelTypeKey__";
public override void OnAuthorization(AuthorizationContext filterContext)
{
var parameters = filterContext.ActionDescriptor.GetParameters();
if (parameters.Length > 0)
{
// store the type of the action parameter so that we could access it later
// in the AuthorizeCore method
filterContext.HttpContext.Items[ModelTypeKey] = new ModelDescriptor
{
Name = parameters[0].ParameterName,
ModelType = parameters[0].ParameterType,
};
}
base.OnAuthorization(filterContext);
}
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var authorized = base.AuthorizeCore(httpContext);
if (!authorized)
{
// the user is not authenticated or authorized => no need to continue
return false;
}
// get the currently authenticated username
string username = httpContext.User.Identity.Name;
// get the id of the resource that he is trying to manipulate
// the id should be sent either as part of the query string or the routes
string id = httpContext.Request.RequestContext.RouteData.Values["id"] as string;
// get the action param type
var modelDescriptor = httpContext.Items[ModelTypeKey] as ModelDescriptor;
if (modelDescriptor == null)
{
throw new InvalidOperationException("The controller action that was decorated with this attribute must take a model as argument");
}
// now load the corresponding entity from your database given the
// username, id and type
object model = LoadModel(id, username, modelDescriptor.ModelType);
if (model == null)
{
// the model that satisfies the given criteria was not found in the database
return false;
}
httpContext.Request.RequestContext.RouteData.Values[modelDescriptor.Name] = model;
return true;
}
private object LoadModel(string id, string username, Type modelType)
{
// TODO: depending on how you are querying your database
// you should load the corresponding model here or return null
// if not found
throw new NotImplementedException();
}
}
and now you could have a controller action that is decorated with this attribute:
[LoadAndAuthorizeResource]
public ActionResult Edit(Picture model)
{
... if we get that far the user is authorized to modify this model
}
I have the following structure in my DB:
DomainEntities:
+EntityID
+Name
+ParentID
+...
Users:
+UserID
+Username
+...
Roles:
+RoleID
+Name
UserRolesAssociation:
+RoleID
+UserID
+EntityID
So i want to use MVC's built in authorization attribute to filter action in my controllers that are made by different members.
I what to be able to say if user1 makes a delete action on entity1 or any entity under it i can see if he has the right role to do that and filter the action accordingly.
What would be the best practice to tackle that topic ?
Should i create my own permissions engine that will provide me the answers i need or can i use the existing capabilities ?
What would be the best practice to tackle that topic ?
A custom [Authorize] seems like a good place to implement this logic.
public class MyAuthorizeAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var authorized = base.AuthorizeCore(httpContext);
if (!authorized)
{
// the use ris not authenticated or not authorized - no need to continue
return false;
}
string username = httpContext.User.Identity.Name;
// read the entity id that this user is attempting to manipulate
string entityId = (string)httpContext.Request.RequestContext.RouteData.Values["id"] ?? httpContext.Request["id"];
return IsAllowed(username, entityId);
}
private bool IsAllowed(string username, string entityId)
{
// You know what to do here - hit the database and check whether
// the current user is the owner of the entity
throw new NotImplementedException();
}
}
and then:
[HttpDelete]
[MyAuthorize]
public ActionResult Delete(int id)
{
...
}
I am trying to follow the Nerd Dinner MVC application as a base to learn the correct way to develop MVC applications.
I have created Interfaces and Repositories as the reference code suggests and am using Entity Framework for data access.
If I want to insert data when a user registers into a table [dbo].[Users], I do not have a controller for Users, how do I do it?
AccountController.cs
[HandleError]
public class AccountController : BaseController
{
[HttpPost]
public ActionResult Register(RegisterModel model)
{
if (ModelState.IsValid)
{
// Attempt to register the user
MembershipCreateStatus createStatus = MembershipService.CreateUser(model.UserName, model.Password, model.Email);
if (createStatus == MembershipCreateStatus.Success)
{
// TODO: Enter record into [Users] get reference to [Aspnet_UserId]
// How do I do this??
//FormsService.SignIn(model.UserName, false /* createPersistentCookie */);
return RedirectToAction("Index", "Home");
}
else
{
ModelState.AddModelError("", ErrorCodeToString(createStatus));
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
}
If I create a UsersController to display views based on the Users table, how would I then add a new record when the user is registering?
I have a separate table [Users] that I wish to populate when a new user registers adding the [Aspnet_UserId] Guid.
You don't need to have a controller for each table in your database. In the code above, the MembershipService is the code that is actually creating the record (via the Repository for users).
The controllers should represent various areas and groups of functionality your website provides. While in many cases, you might have a controller with View, Create, and Update actions that do relate to a specific entity, that does relate to a specific database table, that isn't and shouldn't always be the case.
If it makes sense to have a UsersController because you want to view a list of users, or a specific users profile, that's fine, but the form for creating a user doesn't have to be a part of that controller. Having it be a part of a membership, or admin, or account, or registration controller is ok too.
Update
I'll try to provide you sample code of how I would expect the code to look. But you might have something else in mind, which is fine too, there's no true single way to do these things.
In your code above, I'm not sure what your MembershipService class is doing. It appears there is a static method on it that does something related to User Creation. I would expect that your MembershipService class should be calling your UserRepository to actually do the user creation. But you probably wouldn't want a static class/method for this.
public class MembershipCreationResult
{
public User Member { get; private set; }
public MembershipCreateStatus MembershipCreateStatus { get; private set; }
public MembershipCreationResult(User user, MembershipCreateStatus status)
{
Member = user;
MembershipCreateStatus = status;
}
public bool Success
{
get { return MembershipCreateStatus == MembershipCreateStatus.Success; }
}
}
public class MembershipService
{
public IUserRepository { get; private set; }
public MembershipService(IUserRepository userRepository)
{
UserRepository = userRepository;
}
public MembershipCreateResult CreateUser(string name, string password, string email)
{
User member = UserRepository.Create(name, password, email);
bool status = member != null ? MembershipCreateStatus.Success : MembershipCreateStatus.Failure;
return new MembershipCreationResult(status, member)
}
}
I haven't taken a very close look at the NerdDinner sample, and I haven't used the ASP.NET membership provider, but the concept I have outlined above should work. If MembershipService does something way different from what I have outlined, then you could create a new service to wrap the functionality and leave the existing MembershipService alone.
So I was reading another question regarding login loop when you have a user logging in, set to return to a URL which they might not have access to after logging in (ie. an admin page, and the user logs in with a normal account).
The solution under WebForms seems to be to utilize the UrlAuthorizationModule.CheckUrlAccessForPrincipal method. However that does not work for URLs going to Action Methods secured with the Authorize Attribute. I figured I could work out which method the URL was pointing at and reflect over it to solve my problem - but I can't seem to work out how I get this information out of the routing table.
Anyone ever worked with this, or have a solution for this? If I can just get hold of the route information from a URL I think I could work the rest out, but if anyone has a generic solution - ie. some hidden method akin to the before mentioned one for MVC, then that would be totally awesome as well.
I'm not asking how to check if the User has acces to a specified Controller/Action pair. I first and foremost need to work out how to get the Controller/Action pair from the RouteTable based off the URL. The reason for all the background story, is in case that there does indeed exist an equivalent to UrlAuthorizationModule.CheckUrlAccessForPrincipal for MVC.
John Farrell (jfar)'s answer (SecurityTrimmingExtensions class) updated for MVC 4:
public static class SecurityCheck
{
public static bool ActionIsAuthorized(string actionName, string controllerName)
{
IControllerFactory factory = ControllerBuilder.Current.GetControllerFactory();
ControllerBase controller = factory.CreateController(HttpContext.Current.Request.RequestContext, controllerName) as ControllerBase;
var controllerContext = new ControllerContext(HttpContext.Current.Request.RequestContext, controller);
var controllerDescriptor = new ReflectedControllerDescriptor(controller.GetType());
var actionDescriptor = controllerDescriptor.FindAction(controllerContext, actionName);
AuthorizationContext authContext = new AuthorizationContext(controllerContext, actionDescriptor);
foreach (var authAttribute in actionDescriptor.GetFilterAttributes(true).Where(a => a is AuthorizeAttribute).Select(a => a as AuthorizeAttribute))
{
authAttribute.OnAuthorization(authContext);
if (authContext.Result != null)
return false;
}
return true;
}
}
I ported and hacked this code from the MvcSitemap:
public static class SecurityTrimmingExtensions
{
/// <summary>
/// Returns true if a specific controller action exists and
/// the user has the ability to access it.
/// </summary>
/// <param name="htmlHelper"></param>
/// <param name="actionName"></param>
/// <param name="controllerName"></param>
/// <returns></returns>
public static bool HasActionPermission( this HtmlHelper htmlHelper, string actionName, string controllerName )
{
//if the controller name is empty the ASP.NET convention is:
//"we are linking to a different controller
ControllerBase controllerToLinkTo = string.IsNullOrEmpty(controllerName)
? htmlHelper.ViewContext.Controller
: GetControllerByName(htmlHelper, controllerName);
var controllerContext = new ControllerContext(htmlHelper.ViewContext.RequestContext, controllerToLinkTo);
var controllerDescriptor = new ReflectedControllerDescriptor(controllerToLinkTo.GetType());
var actionDescriptor = controllerDescriptor.FindAction(controllerContext, actionName);
return ActionIsAuthorized(controllerContext, actionDescriptor);
}
private static bool ActionIsAuthorized(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
if (actionDescriptor == null)
return false; // action does not exist so say yes - should we authorise this?!
AuthorizationContext authContext = new AuthorizationContext(controllerContext);
// run each auth filter until on fails
// performance could be improved by some caching
foreach (IAuthorizationFilter authFilter in actionDescriptor.GetFilters().AuthorizationFilters)
{
authFilter.OnAuthorization(authContext);
if (authContext.Result != null)
return false;
}
return true;
}
private static ControllerBase GetControllerByName(HtmlHelper helper, string controllerName)
{
// Instantiate the controller and call Execute
IControllerFactory factory = ControllerBuilder.Current.GetControllerFactory();
IController controller = factory.CreateController(helper.ViewContext.RequestContext, controllerName);
if (controller == null)
{
throw new InvalidOperationException(
String.Format(
CultureInfo.CurrentUICulture,
"Controller factory {0} controller {1} returned null",
factory.GetType(),
controllerName));
}
return (ControllerBase)controller;
}
It could use some caching but for my case that was a premature optimization.
What is the problem you are trying to solve? It sounds like you may be headed down a path to a complex solution that could use a simple solution instead.
If a user doesn't have permissions to access the page after login, are you wanting non-logged in users to go to one page, while logged in users go to a different page?
If that's the case I might be tempted to create another controller for just such scenarios and redirect to that controller anywhere the user doesn't have access. Or if you are using your own base Controller I would put the functionality there.
Then the controller could present the desired view. For example if a non-logged in user tries to access a page they could get redirected to a generic error page. If the user is logged in, they could get redirected to a not authorized page.
This is very similar to Robert's answer.
Here's a basic skeleton for a base controller.
public BaseController: Controller
{
... // Some code
public ActionResult DisplayErrorPage()
{
// Assumes you have a User object with a IsLoggedIn property
if (User.IsLoggedIn())
return View("NotAuthorized");
// Redirect user to login page
return RedirectToAction("Logon", "Account");
}
}
Then in lets say a AdminController (that inherits from BaseController) action
public ActionResult HighlyRestrictedAction()
{
// Assumes there is a User object with a HasAccess property
if (User.HasAccess("HighlyRestrictedAction") == false)
return DisplayErrorPage();
// At this point the user is logged in and has permissions
...
}
This is probably going to sound controversial, but I check security at the beginning of each controller method, inside the method:
public class ProductController : Controller
{
IProductRepository _repository
public ActionResult Details(int id)
{
if(!_repository.UserHasAccess(id))
return View("NotAuthorized");
var item = _repository.GetProduct(id);
if (item == null)
return View("NotFound");
return View(item);
}
}
The reason I don't use the [Authorize] attributes for this is that you cannot pass an id, or any other identifying information, to the attribute at runtime.
In my application I have created a custom filter derived from AuthorizeAttribute, so any unauthorized access will simply go to AccessDenied page. For links, I replace Html.ActionLink with a custom helper Html.SecureLink. In this helper extension, I check for this user's roles access to controller/action against database. If he/she have the authorization, return link otherwise return the link text with special remarks (could be image/ coloring/ js)
Why not attribute your controller methods with the security requirement.
I wrote an attribute to do this as follows:
public class RequiresRoleAttribute : ActionFilterAttribute
{
public string Role { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (string.IsNullOrEmpty(Role))
{
throw new InvalidOperationException("No role specified.");
}
if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
filterContext.HttpContext.Response.Redirect(loginUrl, true);
}
else
{
bool isAuthorised = filterContext.HttpContext.User.IsInRole(this.Role);
<< Complete Logic Here >>
}
}
}
i just spent some time implementing #jfar's solution (updating it for non-deprecated GetFilters() version), and then i realized that i can skip this whole thing.
in my case (and i assume most cases) I have a custom AuthorizationAttribute to implement site authorization which in turn calls my authorization service to make the actual access level determinenation.
So in my html helper for generating menu links, i skipped right to the auth service:
#Html.MenuItem(#Url, "icon-whatever", "TargetController", "TargetAction")
public static MvcHtmlString MenuItem(this HtmlHelper htmlHelper, UrlHelper url,string iconCss, string targetController, string targetAction)
{
var auth = IoC.Resolve<IClientAuthorizationService>().Authorize(targetController, targetAction);
if (auth == AccessLevel.None)
return MvcHtmlString.Create("");
*user is determined within the client auth service
public string GetUser() {
return HttpContext.Current.User.Identity.Name;
}
*could also add some behavior for read-only access. it's nice because my auth service takes care of caching so i don't have to worry about performance.
I am starting to dabble with ASP.Net MVC. One question I have is on best practices for protecting user data. For example in the scenario of Sales people, they should only be able to view their own data.
e.g.
SalesData/Edit/14
It is very easy to change the "14" to view other data which they may/or may not have access to.
At this point, I am thinking in my controllers to check for who is logged in, and checking if they have access to the "id" that is getting requested. The problem I see with this, is that this will be application wide, and I am looking for best practices on how to approach this. Should I be looking at CustomControllers? Filters? or what? Any articles/references for how to tackle this would be appreciated.
Set up your methods for retrieving data from your database repository in such a way that you can pass the UserID of the currently logged in person as a parameter. You can then use a permissions table to filter the data to only that data for which the user has access.
The permissions table would have two fields: UserID and ContentID. Once this is set up, it's fairly straightforward to set up CRUD screens so that someone with administrative privileges can set content permissions.
The problem I see with this, is that
this will be application wide,
Then you need common service that handles it. Suprisingly, I would call it IAuthorisationService.
and I
am looking for best practices on how
to approach this. Should I be looking
at CustomControllers? Filters? or
what?
Whichever way you choose you should use common IAuthorisationService above.
From my experience I can tell that it is easier to inject the service into controller and use it on every action:
/* Interfaces */
public interface IAuthorisationService {
bool CanEdit(YourItem item);
}
public interface ICurrentUserProvider {
YourUserEntity GetCurrentUser();
}
/* Implementations */
public class HttpUserProvider : ICurrentUserProvider {
public YourUserEntity GetCurrentUser() {
return HttpContext.Current.User.Principal as YourUserEntity;
}
}
public calss RolesAuthorisationService : IAuthorisationService {
ICurrentUserProvider userProvider
public RolesAuthorisationService(ICurrentUserProvider userProvider) {
this.userProvider = userProvider;
}
public bool CanEdit(YourItem item) {
var u = userProvider.GetCurrentUser();
if (u == null)
return false;
return item.Owner == u && u.IsInRole("EditYourItem");
}
}
/* Controller */
public class MyController: Controller {
IAuthorisationService authorisation;
public MyController(IAuthorisationService authorisation) {
this.authorisation = authorisation;
}
public ActionResult Edit(int id) {
var item = GetTheItembyIdSomehow();
if (!authorisation.CanEdit(item))
return new HttpUnauthorizedResult();
// Can do this
}
}
Then you can use ControllerFactory to inject the required dependencies automatically into the controllers:
class DependencyInjectionContainer : WindsorContainer {
public DependencyInjectionContainer() {
RegisterDependencies();
}
private void RegisterDependencies() {
// Services
Register(
AllTypes.Of<IDiscoverableService>()
.FromAssembly(typeof(IDiscoverableService).Assembly)
.Configure(c => c.LifeStyle.Transient)
.WithService.FromInterface()
);
// Controllers
Register(
AllTypes.Of<IController>()
.FromAssembly(typeof(DependencyInjectionContainer).Assembly)
.Configure(c => c.LifeStyle.Transient)
);
}
}
class WindsorControllerFactory : DefaultControllerFactory, IDisposable {
private readonly IWindsorContainer container;
public WindsorControllerFactory() {
container = new DependencyInjectionContainer();
}
protected override IController GetControllerInstance(Type controllerType) {
if (controllerType == null)
return base.GetControllerInstance(controllerType);
return (IController) container.Resolve(controllerType);
}
public void Dispose() {
container.Dispose();
}
}
I use IPrincipal and Authorize(Roles='...') attribute to limit access to actions. IPrincipal is then injected into service layer and user IIdentity is used to filter data.
Example: Users create tasks. Every user can see his tasks. GetTask(int taskId) method first filters by CreatedBy field using identifier from IIdentity and then takes task with specified id. If user doesn't have access to data, method will not return any rows.