How to manage a combined role + organization security in MVC?
-
Hi everyone I am fairly new to MVC and I've only done simple projects without security before. I am now working in a new, more complex project that requires a security model based on role + organization. The authorization will be based on Windows user. Once the users are validated, they will be marked as working with their default organization, but the can change the organization at any time. We are imagining a drop-down with the organizations the user belong to, and stored the current one as a session variable. The simplest part of the projects consists of several CRUDs. We have users that are either viewers or editors in these CRUDs, but their role may vary depending on the organization they are working on. E.g. for the "Branches" table, User "Peter" may be an editor for organization 1 and a viewer for organizations 2 and 3. If I ignore the organization, I can easily manage the security by extending System.Web.Security.RoleProvider and handling the logic within that class. I believe I could read the current organization from the session variable (haven't tried that yet) in order to add that to the class logic, but I'm pretty sure there must be a better solution. Can anyone suggest a better solution? Ideally it should be following the ASP.NET System.Web.Security model. Any Visual Studio integrated framework that makes this security handling easier is also very welcomed. Thank you
-
Hi everyone I am fairly new to MVC and I've only done simple projects without security before. I am now working in a new, more complex project that requires a security model based on role + organization. The authorization will be based on Windows user. Once the users are validated, they will be marked as working with their default organization, but the can change the organization at any time. We are imagining a drop-down with the organizations the user belong to, and stored the current one as a session variable. The simplest part of the projects consists of several CRUDs. We have users that are either viewers or editors in these CRUDs, but their role may vary depending on the organization they are working on. E.g. for the "Branches" table, User "Peter" may be an editor for organization 1 and a viewer for organizations 2 and 3. If I ignore the organization, I can easily manage the security by extending System.Web.Security.RoleProvider and handling the logic within that class. I believe I could read the current organization from the session variable (haven't tried that yet) in order to add that to the class logic, but I'm pretty sure there must be a better solution. Can anyone suggest a better solution? Ideally it should be following the ASP.NET System.Web.Security model. Any Visual Studio integrated framework that makes this security handling easier is also very welcomed. Thank you
I'm no expert at this by far. But I'll mention the HttpContext. It has a lifespan of one cycle and is stateless. So I made a Attribute that you decorate the controller ActionResult with. It runs run before the ActionResult is fired, and passes everything downstream. Basically it's session free so you don't have to worry about restarts or what server you hit. I do remember this being a little buggy, in terms of having to erase the cookie a couple of times when exiting debug and starting again, but I think I fixed it.
[AdminCheck]
public IActionResult SomeEditor()The AdminCheck reads the cookie with special data in it, and if the user authenticates, it creates a new HttpContext.User using a GenericIdentity and GenericPrincipal I wrote this for .Net Core
[AttributeUsageAttribute(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
public class AdminCheckAttribute : ActionFilterAttribute, IActionFilter
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var controller = filterContext.Controller as Controller;
var httpContext = controller.HttpContext;Model\_Admin\_Login pResult = SecurityCookies.CookieRead\_Admin\_Login(httpContext); if (pResult.AccountName != null) { Model\_Admin\_Login model = new Model\_Admin\_Login(); bool result = EF\_Website\_Users.AdminCheck\_AccountName(pResult.AccountName, ref model); if (true == result) { // Program the HttpContext.Current // This will persist for the current cycle only String\[\] newRoles = { "Administrator" }; GenericIdentity newIdentity = new GenericIdentity(pResult.AccountName); GenericPrincipal newPrincipal = new GenericPrincipal(newIdentity, newRoles); httpContext.User = newPrincipal; System.Threading.Thread.CurrentPrincipal = httpContext.User; // Store the Global ViewData to Show the Admin Menu or Not controller.ViewData\["Admin\_Menu"\] = true; controller.ViewData\["Admin\_UserID"\] = model.ID; controller.ViewData\["Admin\_UserName"\] = pResult.AccountName.ToLower(); controller.ViewData\["Admin\_ImageUrl"\] = model.Avatar.Url; controller.ViewData\["Admin\_ImageAlt"\] = model.Avatar.Alt; controller.ViewData\["Admin\_Base64"\] = mode
-
I'm no expert at this by far. But I'll mention the HttpContext. It has a lifespan of one cycle and is stateless. So I made a Attribute that you decorate the controller ActionResult with. It runs run before the ActionResult is fired, and passes everything downstream. Basically it's session free so you don't have to worry about restarts or what server you hit. I do remember this being a little buggy, in terms of having to erase the cookie a couple of times when exiting debug and starting again, but I think I fixed it.
[AdminCheck]
public IActionResult SomeEditor()The AdminCheck reads the cookie with special data in it, and if the user authenticates, it creates a new HttpContext.User using a GenericIdentity and GenericPrincipal I wrote this for .Net Core
[AttributeUsageAttribute(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
public class AdminCheckAttribute : ActionFilterAttribute, IActionFilter
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var controller = filterContext.Controller as Controller;
var httpContext = controller.HttpContext;Model\_Admin\_Login pResult = SecurityCookies.CookieRead\_Admin\_Login(httpContext); if (pResult.AccountName != null) { Model\_Admin\_Login model = new Model\_Admin\_Login(); bool result = EF\_Website\_Users.AdminCheck\_AccountName(pResult.AccountName, ref model); if (true == result) { // Program the HttpContext.Current // This will persist for the current cycle only String\[\] newRoles = { "Administrator" }; GenericIdentity newIdentity = new GenericIdentity(pResult.AccountName); GenericPrincipal newPrincipal = new GenericPrincipal(newIdentity, newRoles); httpContext.User = newPrincipal; System.Threading.Thread.CurrentPrincipal = httpContext.User; // Store the Global ViewData to Show the Admin Menu or Not controller.ViewData\["Admin\_Menu"\] = true; controller.ViewData\["Admin\_UserID"\] = model.ID; controller.ViewData\["Admin\_UserName"\] = pResult.AccountName.ToLower(); controller.ViewData\["Admin\_ImageUrl"\] = model.Avatar.Url; controller.ViewData\["Admin\_ImageAlt"\] = model.Avatar.Alt; controller.ViewData\["Admin\_Base64"\] = mode
Thanks for your reply Jim. I will look into the example you provided. When you said "It has a lifespan of one cycle and is stateless.", you mean that it won't survive a IIS/Server reboot? As long as it keeps track of sessions (which I'm pretty sure it does), we are OK with it. Thanks you
-
Thanks for your reply Jim. I will look into the example you provided. When you said "It has a lifespan of one cycle and is stateless.", you mean that it won't survive a IIS/Server reboot? As long as it keeps track of sessions (which I'm pretty sure it does), we are OK with it. Thanks you
On the contrary quite the opposite. Because it reads the cookie you set with say the user name and a special token you craft when validated or authenticated the first time, the attribute will revalidate the authentication being stateless and durable, it will survive a server reboot, or say a worker process recycle and can be used in multiple Docker containers using Kubernetes. Just add another column to your user database table to store a token that you carefully craft. Using Sessions to store a value or to maintain authentication is dangerous and not durable in an environment that is suppose to be stateless. It may work today, but can be the cause of your worst mistake ever.
If it ain't broke don't fix it Discover my world at jkirkerx.com