Authorize attribute Roles from Database - c#

I want to get the below roles(Admin,IT,..) from the database without hard coding on top of the action result. Please provide any help.
[Authorize(Roles = "Admin,IT")]
public ActionResult Index()
{
}

There aren't any super-easy ways to do this. You can apply the [Authorize] attribute to a controller instead of an action, but it is still "hard-coding" it.
You could create a custom Authorization attribute ([link])1, but you would have to store the Routing values in the database, as well as the Roles that were allowed to access the route. However this just shifts the burden of making manual changes into the database from the code.
I don't really think that this should really be considered "Hard Coding" as you have to declare your authorization somewhere, and you can still have different users with different permissions in different environments. Who else but the developer should know best which routes require which authorization? Would you want to break your access control because you changed the routing somewhere?

create an Action finter
public class ValidationPermission : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if(System.Web.HttpContext.Current.Session["UserName"] == null)
System.Web.HttpContext.Current.Response.RedirectToRoute("Login");
else{
// code check CheckPermission
}
}
}
Action controller
[ValidationPermission(Action = ActionEnum.Read, Module = CModule)]
public ActionResult Index()
{
// something code
}

You can try with this way
public static Role {
public static string Admin ="Admin";
public static string IT ="IT";
}
[Authorize(Roles = Role.Admin,Role.IT)]
public ActionResult Index()
{
}

Related

Use [Authenticate] attribute in MVC controller using sessions to authorize users?

I have been searching for similar solutions online but everything seems overcomplicating, currently, I have a UserController that I only want users that are logged in to access, my current solution involves using if statements however I was wondering if it's possible to use the [Authorize] attribute and apply it to methods or the entire controller perhaps?
public class UserController : ASessionController {
public UserController (IAmazonDynamoDB dynamoDbClient, DynamoDBContext dynamoDbContext) : base(dynamoDbClient, dynamoDbContext) {
}
// [Authorize]
public IActionResult Index () {
// check session variable
if(!UserIsLoggedIn()){ /*redirect to sign in*/ }
return View();
}
}
Perhaps I am not understanding if this is the purpose of the Authorize attribute? Thank you in advance.
You can use the Authorize attribute on endpoints and / or the Controller itself
It will force the User to be authenticated to again access to the decorated item.
In addition you can also restrict it to authenticated users with a given or multiple roles like in the example
[Authorize(Roles = "Administrator")]
public IActionResult Index()
{
...
}
[Authorize(Roles = "Administrator,Guest")]
public IActionResult NotAnIIndex()
{
...
}
But you should read the Microsoft Documentation tuturial

Authorization with Session variables in asp net mvc 5

So my project requirements changed and now I think I need to build my own action filter.
So, this is my current login controller:
public class LoginController : Controller
{
// GET: Login
public ActionResult Index()
{
return View();
}
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginViewModel model)
{
string userName = AuthenticateUser(model.UserName, model.Password);
if (!(String.IsNullOrEmpty(userName)))
{
Session["UserName"] = userName;
return View("~/Views/Home/Default.cshtml");
}
else
{
ModelState.AddModelError("", "Invalid Login");
return View("~/Views/Home/Login.cshtml");
}
}
public string AuthenticateUser(string username, string password)
{
if(password.Equals("123")
return "Super"
else
return null;
}
public ActionResult LogOff()
{
Session["UserName"] = null;
//AuthenticationManager.SignOut();
return View("~/Views/Home/Login.cshtml");
}
}
And this is my action filter attempt:
public class AuthorizationFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (HttpContext.Current.Session["UserName"] != null)
{
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary{{ "controller", "MainPage" },
{ "action", "Default" }
});
}
base.OnActionExecuting(filterContext);
}
}
I have already added it to FilterConfig, but when I login it does not load Default.cshtml it just keeps looping the action filter. The action result for it looks like this:
//this is located in the MainPage controller
[AuthorizationFilter]
public ActionResult Default()
{
return View("~/Views/Home/Default.cshtml");
}
So, what would I need to add in order to give authorization so only authenticated users can view the applicationĀ“s pages? Should I use Session variables or is there another/better way of doing this using? I am pretty much stuck with AuthenticateUser(), since what happens there now is just a simple comparison like the one we have there now.
Thank you for your time.
Create an AuthorizeAttribute with your logic in there:
public class AuthorizationFilter : AuthorizeAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext.ActionDescriptor.IsDefined(typeof(AllowAnonymousAttribute), true)
|| filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(typeof(AllowAnonymousAttribute), true))
{
// Don't check for authorization as AllowAnonymous filter is applied to the action or controller
return;
}
// Check for authorization
if (HttpContext.Current.Session["UserName"] == null)
{
filterContext.Result = new HttpUnauthorizedResult();
}
}
}
As long as you have the Login URL Configured in your Startup.Auth.cs file, it will handle the redirection to the login page for you. If you create a new MVC project it configures this for you:
public partial class Startup
{
public void ConfigureAuth(IAppBuilder app)
{
app.UseCookieAuthentication(
new CookieAuthenticationOptions {
// YOUR LOGIN PATH
LoginPath = new PathString("/Account/Login")
}
);
}
}
Using this you can decorate your controllers with [AuthorizationFilter] and also [AllowAnonymous] attributes if you want to prevent the authorization from being checked for certain Controllers or Actions.
You might want to check this in different scenarios to ensure it provides tight enough security. ASP.NET MVC provides mechanisms that you can use out of the box for protecting your applications, I'd recommend using those if possible in any situation. I remember someone saying to me, if you're trying to do authentication/security for yourself, you're probably doing it wrong.
Since your attribute is added to the FilterConfig, it will apply to ALL actions. So when you navigate to your MainPage/Default action it will be applying the filter and redirecting you to your MainPage/Default action (and so on...).
You will either need to:
remove it from the FilterConfig and apply it to the appropriate actions / controllers
or add an extra check in the filter so that it doesn't redirect on certain routes

How to redirect all pages in my mvc asp.net web app except for one role?

I work with asp.net c# mvc framework. I need a way to 'turn-off' my web app for all users except administrator (i. e. all pages should return to something like "The application is closed" for all the roles except Admin).
I already create a button in order to save the status of the web app (ON/OFF) in a DB.
Do I have to check on each page the status of the application ?
Is-it possible to have a global redirection except for one role ?
I don't know how to properly do this global closure. Any suggestions are welcomed.
I can think of three approaches to check and do a redircet
An HttpModule hooked into the appropriate, post-authorisation event. Presumably PostAuthorizeRequest of HttpApplication.
In your "global" (Global.aspx.cs) subscribe to that same event.
An MVC Action filter, overriding OnActionExecuting. (Ensure you make it global, to avoid needing to apply to every controller: add to GlobalFilters.Filters in your Application_Start.)
Of these 3 is part of MVC, but is much later in the pipeline (much more work will have been done, to be thrown away when the filter fails).
Use of a module is controlled by configuration which would make is easier to switch on and off.
option 2 is likely easiest to implement, but I would tend to prefer the modularity that 1 gives.
You can accomplish your requirement with the help of custom filters shown below :-
[CheckUserRole]
public class YourController : Controller
{
public ActionResult YourAction()
{
}
}
public class CheckUserRoleAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
// Get the User Id from the session
// Get Role associated with the user (probably from database)
// Get the permission associated with the role (like Read, write etc)
// if user is not authenticated then do as :
filterContext.Result = new RedirectToRouteResult(new
RouteValueDictionary(new { controller = "Error", action = "AccessDenied" }));
}
}
Did you tryActionFilterAttribute ?
Here is a basic example:
Your controller:
[IsAdmin]
public class YourController
{
}
Your attribute
public class IsAdminAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if () // Check that your user is not an Admin and that your application is "turn-off"
{
filterContext.Result = new HttpStatusCodeResult(403); // or whatever you want
}
}
}
Add [IsAdmin] on top of all your controllers.
You can write in all other Controllers which are used as follows..
public class HomeController : Controller
{
public ActionResult Index()
{
if (User.IsInRole("Administrator"))
return RedirectToAction("PagetoRedirect");
else
return RedirectToAction("CommonPagetoShowApplicationAsClosed");
}
}
Or
Action Filter, you can create on your own and look for named action like IndexRolename

Best practise for very similar controllers in different areas

My asp.net MVC app has an area "Company" and an area "Admin".
A company can do CRUD on its users in the company area. I've created a UsersController for this in the Company area.
An admin can do CRUD on a company's users in the Admin area. I've created a CompanyUsersControllers for this in the Admin area.
Both controllers have extremely similar code and I was wondering what the cleanest way is to reuse most of the code.
While writing this question, I figured I could create an abstract UsersControllerBase class with virtual ActionResults. I did this and it works for the Company area. I define attributes on the overriding methods in the UsersController class and call the corresponding abstract method in every overriding method.
Here is an example from the base class:
[UsersControllerBase.cs]
public virtual ActionResult Edit(string slug)
{
var user = UserRepository.GetBySlug(slug);
if (user.CompanyId != CurrentUser.CompanyId)
{
throw new SecurityException(CurrentUser.Id + " attempted to edit a user that does not belong to his company");
}
var model = user.ToViewModel();
AddListsTo(model);
return View(model);
}
And the corresponding override:
[Company/UsersController.cs]
[HttpGet, GET("/company/users/{slug}/edit")]
public override ActionResult Edit(string slug)
{
return base.Edit(slug);
}
The problem is that the Edit in Admin/CompanyUsersController.cs has an extra parameter "companySlug" which is used to find the company for which we are currently editing users.
As you can see in the code above, in Company/Userscontroller.cs we simply derive the company from the CurrentUser.
What would be the best approach to handle this problem?
td;dr
I have 2 controllers with identically named actions that have near-identical method bodies but different parameters. I want to reuse the code as much as possible. pls how do I c#.
If the two methods have different signatures, I don't think it's really worth implementing it as a base class method, though it's not impossible. I would create a protected helper method on the base class and put the shared code in that. Like this (making a few assumptions about your Repository API):
[UsersControllerBase.cs]
protected virtual ActionResult Edit(User user)
{
var model = user.ToViewModel();
AddListsTo(model);
return View(model);
}
[Admin/CompanyUsersController.cs]
[HttpGet, GET("/admin/users/{companySlug}/{slug}/edit")]
public ActionResult Edit(string companySlug, string slug)
{
var user = UserRepository.GetBySlug(companySlug, slug);
return base.Edit(user);
}
[Company/UsersController.cs]
[HttpGet, GET("/company/users/{slug}/edit")]
public ActionResult Edit(string slug)
{
var user = UserRepository.GetBySlug(slug);
if (user.CompanyId != CurrentUser.CompanyId)
{
throw new SecurityException(CurrentUser.Id + " attempted to edit a user that does not belong to his company");
}
return base.Edit(user);
}
If the Edit action in the other controller has an extra parameter, then it shouldn't be an override of the base Edit action, in my opinion. I would create a separate Edit action in the derived controller with two parameters, and make the override Edit action return 404.
[HttpGet]
public override ActionResult Edit(string slug)
{
return HttpNotFound();
}
[HttpGet]
public ActionResult Edit(string slug, string companySlug)
{
// some code...
}
It is not worth implementing a basecontroller because the similar methods have different parameters.
Even if they would have the same signature, the code is cleaner, more readable, understandable and maintainable when focusing on keeping the controllers as lightweight as possible instead of adding so much complexity to save a few duplicate lines of code.

Lock Down ASP.NET MVC App Administration Site to LocalHost only

I have an ASP.NET MVC website that I would like to add a small administration page to. The issue I have is that I will be deploying this all over and I will not have SSL available. I am OK with requiring the administrator to remote desktop and use the local browser to perform the administration.
Can this be done? I would basically like to get the same behavior as <customeErrors mode="RemoteOnly" /> except for my administration pages. Can I do this via web.config some how?
Request.IsLocal is your friend.
http://msdn.microsoft.com/en-us/library/system.web.httprequest.islocal.aspx
You can use that to check that a request is coming from the local machine.
Custom Attribute
You could then extend this to be a custom attribute, but that might be overkill. If that is the route you choose this is a good example that does something similar:
Custom Attributes on ActionResult
MVC3 onwards allows you to set an attribute at Controller level, rather than Method too, so you could lock access to the entire controller responsible for the admin pages.
I did it by writing a custom attribute, like this:
public class IsLocalAttribute : AuthorizeAttribute
{
public bool ThrowSecurityException { get; set; }
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var isLocal = httpContext.Request.IsLocal;
if (!isLocal && ThrowSecurityException)
throw new SecurityException();
return isLocal;
}
}
Basic usage on an entire controller:
[IsLocal]
public class LocalOnlyController : Controller
{
public ActionResult Index()
{
return View();
}
}
or on a specific method:
public class SomeController : Controller
{
[IsLocal]
public ActionResult LocalOnlyMethod()
{
return View();
}
}
If you want to throw a security exception instead of a 302 redirect:
public class SomeController : Controller
{
[IsLocal(ThrowSecurityException = true)]
public ActionResult LocalOnlyMethod()
{
return View();
}
}

Categories