56109

Custom security scenario in ASP.NET MVC

Question:

I don't have a lot of experience with this and I am really hoping to get a good suggestion from you guys. I need to implement the following security scenario and I would like to know the best way to do it.

Imagine we have Employees, Supervisors and Department managers. Both Employees and Supervisors have ManagerId assigned based off and pointing to the department manager they belong to.

When a supervisor user logs in I want him to only see records for employees that belong to the same ManagerId as his. If another supervisor with another ManagerId user logs in and manually punches other employee's information in url (ex: wwww.domain.com/employee/details/{id} ), because his ManagerId != employee's ManagerId I would like the access to be restricted.

Does it make sense ?

I started typing out checks on all ActionMethods such as:

public ActionResult Details(int id) { var employee = employeeRepository.Get(id) var user = (CustomIdentity)ControllerContext.HttpContext.User.Identity; if(employee.managerId == user.managerId) { Do whatever... } else { Not allowed } }

But typing that out in all ActionMethods seems redundant and just..ehh... I know there must be a better way.

Answer1:

Here is a stab at a solution. It needs a bit of cleanup but should give you everything you need.

Create a custom ActionFilter, and then decorate your methods with it.

[ManagerIdAuthentication] public ActionResult Details(int id) { // Gets executed if the filter allows it to go through. }

The next class can be created in a separate library so you can include it in all your actions that require this validation.

public class ManagerIdAuthentication : ActionFilterAttribute { public override void OnActionExecuting(ActionExecutingContext filterContext) { // the next line needs improvement, only works on an httpGet since retrieves // the id from the url. Improve this line to obtain the id regardless of // the method (GET, POST, etc.) var id = filterContext.HttpContext.Request.QueryString["id"]; var employee = employeeRepository.Get(id); var user = filterContext.HttpContext.User.Identity; if (employee.managerId == user.managerId) { var res = filterContext.HttpContext.Response; res.StatusCode = 402; res.End(); filterContext.Result = new EmptyResult(); //may use content result if want to provide additional info in the error message. } else { // OK, let it through. } } }

Answer2:

I had a similar issue in the past, what I would consider per-object permissions. What I did was add a member to the object similar to:

public bool CanUserAccess(User user) { return managerId == user.managerId; }

Then, at the top of each action providing access to a controlled resource:

public ActionResult Details(int id) { var employee = employeeRepository.Get(id) var user = (CustomIdentity)ControllerContext.HttpContext.User.Identity; if(!employee.CanUserAccess(user)) return new HttpUnauthorizedResult(); // Normal logic here }

It's certainly not perfect, but it does centralize the permission handling and allows you to easily increase the complexity in the future (allow access up the chain, special rules for HR, etc.). You could also write another overload/extension to access the User.Identity property for a bit more automation (or at least handle the type conversions).

Since I was dealing with ACL's, I would have additional methods/parameters to specify the basic nature of the action (e.g. Read, Write, Delete, Create, etc.).

Recommend

  • Excel Result not giving me anything, but it runs fine
  • A clean solution to use multiple submit button in ASP.NET MVC
  • “Value cannot be null or empty. Parameter name: contentPath” on a most unexpected line on postback w
  • Overriding view location for Razor View Engine
  • HttpContext.Current.User null with Web API v2 only project
  • Why does Request.QueryString[“path”] converts all + signs to spaces?
  • X509 store can not find certificate by SerialNumber
  • How to filter local requests in asp.net web api?
  • Reference generic url parameter in AuthorizeAttribute
  • Not quite understanding the query after just shifting column names
  • ZF2: How to get Zend\\Navigation inside custom route?
  • iOS two views cover exactly half of parent view
  • I get the following error when trying to set a wallpaper [duplicate]
  • mysql auto kill query
  • How to get or calculate size of Azure File/Share or Service
  • How do I install a previous version of build tools in addition to the sdk?
  • Entity Framework Core 1.0.1 add-migration
  • SharePoint REST query SP.UserProfiles.PeopleManager
  • Insert records if not exist SQL Server 2005
  • CS1703: In Xamarin.Droid, should I use the .Net Standard windowsruntime.dll located in Mono.Framewor
  • CoreData basics – to-many relationship array data
  • Neo4j: Legacy Indexes and auto index vs new label bases schema indexes
  • Fragment gives me an error while inflating 1
  • Strong vs Weak entities MYSQL
  • Using android opencv apps without downloading opencv sdk manager
  • Owin Authentication and claims in asp.net how to access user data
  • msbuild create itemgroup from property group
  • How to detect interior vertices in groups of 2d polygons? (E.g. ZIP Codes to determine a territory)
  • how to avoid repetitive constructor in children
  • Should I or shouldn't I use the CachingConnectionFactory with hornetq 2.4.1
  • Saving Changes After In-App Purchase Has Been Purchased
  • How to install a .deb file on a jailbroken iphone programmatically?
  • Regex thinks I'm nesting, but I'm not
  • Getting last autonumber in access
  • Large data - storage and query
  • Turn off referential integrity in Derby? is it possible?
  • Authorize attributes not working in MVC 4
  • Busy indicator not showing up in wpf window [duplicate]
  • Python/Django TangoWithDjango Models and Databases
  • Net Present Value in Excel for Grouped Recurring CF