How to authorize one method differently than the controller? (MVC) - c#

On a project I am currently working on I have three roles: Admin, Super-user and User.
Admins can delete users, delete comments etc, but Super-users must be able to delete comments too. So I built an AdminController and Authorized it with Admin. One method must be accessable with the Super-user, so I authorized it for the Super-user but that won't work.
[Authorize(Roles = "Admin")]
public class AdminController : Controller
{
(... here be only admin methods ...)
[HttpGet]
[Authorize(Roles = "Super-user")]
public ActionResult Delete()
{
//deletes a comment
return View();
}
(... here be only admin methods ...)
}
I looked into overriding the Authorize attribute but I'm trying to find a solution where that is not needed, any idea's on how to authorize just one method for the Super-user?
The Admin as the Super-user must have access to the method Delete().
Thanks!

I think as #Felix Cen pointed specifying this should work at controller
[Authorize(Roles = "Admin, Super-user")]
and then specify this at the method level
[Authorize(Roles = "Super-user")]
Alternatively you can use a deny attribute as pointed out in this question
public class DenyAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext) {
if (httpContext == null) {
throw new ArgumentNullException("httpContext");
}
IPrincipal user = httpContext.User;
if (!user.Identity.IsAuthenticated) {
return false;
}
if (Users.Length > 0 && Users.Split(',').Any( u => string.Compare( u.Trim(), user.Identity.Name, StringComparer.OrdinalIgnoreCase))) {
return false;
}
if (Roles.Length > 0 && Roles.Split(',').Any( u => user.IsInRole(u.Trim()))) {
return false;
}
return true;
}
Another question here(i think that i sort of like this specific answer here)
My personal take on this would be to split the controller. Just create
another controller For the actions you don't need authentication.
Or you could have :
BaseController
doesn't require authentication - here you have all your "base stuff" :).
BaseAuthController : BaseController
all actions here require authentication.
That way you can have authentication when you want , just by deriving
from a specific class.

Related

MVC C# preventing normal users from accessing admin control URL - No roles

Right now, my user table has a bool called Admin. As the code shows, if user.admin = true, the user is able to see the admin area button and access it.
#if (Common.UsuarioLogueado.Admin) {
<li>Admin control panel</li>
}
This is working as intended. However, non admin users can still go to the control panel by accessing it´s url http://localhost/appName/admin/ClientesAdmin/list
How do I prevent such thing? I was thinking about showing an error msg
Going along with the other answers about using Roles, and the AuthorizeAttribute.. which in my opinion is the better way to achieve what you're trying to do, there is another way.
You could just simply redirect the user to another page.. preferable an error page saying you don't have access to the requested page, or just a 401 page which the AuthorizeAttribute would do if you weren't authorized.
Alternate Solution
public class ClientesAdmin : Controller {
// [Authorize(Roles="Admin")] could do it this way
public ActionResult List() {
// or..
if(!Common.UsuarioLogueado.Admin)
{
return new HttpStatusCodeResult(401);
// or
// return View("Error") // usually there is an 'Error' view the Shared folder
}
return View();
}
}
This is not the best solution but I don't know how far along your project is, but simply an alternate solution.
This is how I do it. However your membership system needs to be using ASP.Net Roles for this to work properly.
In your controller you just add the data annotation Authorize. for the function to be accessed by the client, they must be logged in and have the roll specified in the function.
This solution may not be direct cut and paste, but you can see the basic usage then perhaps do a little more research on the Authorize functionality.
public class MyController : Controller {
[Authorize(Roles="Admin")]
public ActionResult AdminIndex() {
return View();
}
[Authorize(Roles = "basic")]
public ActionResult BasicUsersIndex() {
return View();
}
}
Ideally you should be using role based access control. By limiting access by the role, rather than a boolean value in a table you could decorate your CientesAdmin controller with an Authorize Attribute like below.
[Authorize(Roles = "Admin")]
public class CientesAdminController : Controller
{
}
You could also use razor helpers to check if a user IsInRole("Admin").
There is a lot of help on the net to guide you down this path, but if your app is already developed you probably want to stage your changes. Then the recommendation would be to create your own AuthoriseAttribue. Something like.
public class RestrictAccessToAdmins : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
//Do the default Authorise Logic (Check if user is loggedin)
base.AuthorizeCore(httpContext);
if (httpContext.User.IsInRole("Admin")) return true;
var id = httpContext.User.Identity.GetUserId();
using (ApplicationDbContext context = new ApplicationDbContext())
{
//Implement you own DB logic here returning a true or false.
return context.Common.First(u => u.userid == id).UsuarioLogueado.Admin;
}
}
}
To use the attribute you'd do the following.
[RestrictAccessToAdmins]
public class CientesAdminController : Controller
{
}
Then over time, with better understanding of the default authorise attribute and a bit of refactoring you could easily change the attribute to below :)
[RestrictAccessToAdmins(Roles = "Admin")]
public class CientesAdminController : Controller
{
}

C# mvc5 - Easy way to check if user is authenticated in each controller method

I have a controller that I only want authenticated users to be able to access. Do I have to put a check in each method in my controller to verify a user is authenticated, or is there another way to handle this? Can I use annotations to do this instead?
Example from my controller:
public ActionResult Index()
{
if (UserVerified())
{
...
}
return RedirectToAction("Login", "Account");
}
public ActionResult FacebookLogin()
{
if (UserVerified())
{
....
}
return RedirectToAction("Login", "Account");
}
private bool UserVerified()
{
if (User != null && User.Identity != null && User.Identity.IsAuthenticated)
{
return true;
}
return false;
}
You can use AuthorizeAttribute for it.
Put it to every action.
[Authorize]
public ActionResult Index()
{
}
[Authorize]
public ActionResult FacebookLogin()
{
}
It will do the whole work for you. It checks whether the currect user is authenticated. If he is authenticated - proceeds to the action, if he is not - returns to the home page.
You can also add this attribute to a controller. Then all actions will require authorization.
[Authorize]
public class HomeController
{
public ActionResult Index()
{
}
public ActionResult FacebookLogin()
{
}
}
Update: And, yes, as Kamil said. Read this article, please.
http://www.asp.net/web-api/overview/security/authentication-and-authorization-in-aspnet-web-api
You spend some time now and will spend much less time having questions about ASP.NET authentication in future.
By the way, you don't need to check for
User != null && User.Identity != null
If you are using default authentication then you can be always sure that User.Identity is a proper object. You can access User.Identity.IsAuthenticated directly.
Using Authorize attribute is way to go (already answered here). In addition, if you may want to implement some other business rules or filtering checks, you can create a filter class inheriting from AuthorizeAttribute.
e.g.
public class CustomAuthorizeFilter: AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var isAuthorized = base.AuthorizeCore(httpContext);
if (!isAuthorized)
{
return false; //User not Authorized
}
else
{
//Check your conditions here
}
}
}
Then decorate your controller or Action as:
[CustomAuthorizeFilter]
public class SomeController
{
}
You can either user [Authorize] attribute which is inbuilt. Or you can develop your custom attribute for the same purpose.
You can start from here for your own attribute:
Create custom attribute
If you want to perform validation on each action method, then put that attribute on Controller level rather than each action method.
You can use [Authorize] attribute above controller methods.
Please follow this link
If you want the authentication rules to apply to all controller actions you can do this:
[someAuthAttribute]
public class HomeController : Controller
{
// pseudo
public ActionResult Index() {
return response;
}
public ActionResult FacebookLogin(){
return response;
}
}
Where Index() and FacebookLogin() will adhere to the authentication rules of [someAuthAttribute]. You can also use this "hierarchy" to apply more specific rules to your action methods. Like this:
[someAuthAttribute]
public class HomeController : Controller
{
// pseudo
public ActionResult Index() {
return response;
}
[someFBAuthAttribute]
public ActionResult FacebookLogin(){
return response;
}
}
You can inherit authorization from a base controller.
[Authorize(Roles = #"Domain\Group")]
public class BaseController : Controller
public class ChildController : BaseController

MVC Authorize attribute must meet all requirements, is is possible to meet only one?

I have a view that I want to be able to be accessed by admins or specific users. So I added this attribute.
[Authorize(Roles = "Admin", Users = "john, jane, jim" )]
I was hoping that a user would be authorized if they met either the role OR the user. But it seems to only authorize users that match both criteria.
Is it possible to handle it as an or?
I just created a custom authorize class that handles the unauthorized request and does it's own comparison. I've also added a redirect to a custom unauthorized view instead of just throwing out a 401 error.
public class MyAuthorizeAttribute : AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
// The user is not authenticated
base.HandleUnauthorizedRequest(filterContext);
}
else if (!Roles.Split(',').Any(filterContext.HttpContext.User.IsInRole) && !Users.Split(',').Any(ex => ex.ToLower().Trim() == filterContext.HttpContext.User.Identity.Name.ToLower()))
{
// The user is not in any of the listed roles =>
// show the unauthorized view
filterContext.Result = new ViewResult
{
ViewName = "~/Views/Shared/Unauthorized.cshtml"
};
}
}
}
Then I just use MyAuthorize instead of Authorize in the attribute like so
[MyAuthorize(Users = "john, jane, tim", Roles = "admin")]

Authorize attribute for any roles

Let's say I have WebApi Controller
[Authorize]
public class SomeApiController : ApiController
Controller action methods itself does not have any [Authorize] or [AllowAnonymous] attributes.
I want Authorize attribute to return 401 (Unauthorized) error if user has no roles - seems logical (if user had role and now doesn't have ANY - he shouldn't be allowed to perform action even though user is authenticated). I have looked to asp.net mvc webstack I have found the following code in Authorize attribute:
if (_rolesSplit.Length > 0 && !_rolesSplit.Any(user.IsInRole))
{
return false;
}
So looks like if we didn't passed roles authorize attribute just checks if user is authenticated. Setting each role in Roles list is not an option for me ( I mean [Authorize(Roles="role1,role2,...")]).
Therefore question - can I somehow achieve setting Authorize attribute to check if user has ANY role. Or it's better to write custom attribute inherited from above?
Create custom attribute like below:
public override void OnAuthorization(AuthorizationContext filterContext)
{
string[] userRoles = System.Web.Security.Roles.GetRolesForUser(filterContext.HttpContext.User.Identity.Name);
if (!userRoles.Any())
{
throw new HttpException(401, "Unauthorized");
}
base.HandleUnauthorizedRequest(filterContext);
}
}
Hope it helps

Role Management in MVC3

I want to add a functionality to application such that only admin can create users and he can provide access to particular pages to user.
He can create roles and can provide users different roles.
I am using Visual Studio 2010 and building this application in MVC3.
Please give me suggestions to make over it.
Thanks in advance.
1.Decorate your user creation and permission setting actions with Authorize attribute
(Notify, that usage of Roles property of AuthorizeAttribute requires implementation of MembershipProvider (standart or custom) and registering it in web.config)
public class AccountController : Controller
{
[HttpGet, Authorize(Roles = "Admin")]
public ViewResult CreateUser()
{
return View();
}
[HttpPost, Authorize(Roles = "Admin")]
public ActionResult CreateUser()
{
//... call service method to create user
}
[HttpPost, Authorize(Roles = "Admin")]
public ActionResult AssignPageToUser(int userId, string controllerName, string ActionName)
{
//... insert record into table (UserPermissions) with attributes (userId, actionName, controllerName)
}
// other methods without decoration by authorize attribute
}
Next paragraphs are correct if you really want to have full control on action permissions separately for each user.
If you think, that your permissions can group in finite and small number on roles - you can decorate all actions/controllers by authorize attribute and specify roles, for which action/controller available: [Authorize("Customer, Manager, RegionalAdmin")] and give admin possibility to assign roles to users. But remember, that in is enough to be in only 1 of listed roles to get access, you can't require by this attribute, for example and Admin, and Manager roles.
If you want to require necessarily more than 1 role, use multiple attributes:
public class MyController:Controller
{
[Authorize(Roles = "Manager")]
[Authorize(Roles = "Admin")]
public ActionResult Action1()
{
//...
}
}
2.For your pages you can create your own filter attribute, inherited from authorize attribute, that will check, if action is available for user (i think you want to assign actions but not views to user).
public UserPermissionRequiredAttribute: AuthorizeAttribute
{
public OnAuthorization(AuthorizationContext filterContext)
{
var isAuthenticated = filterContext.HttpContext.User.Identity.IsAuthenticated;
var userName = filterContext.HttpContext.User.Identity.Name;
var actionName = filterContext.ActionDescriptior.ActionName;
var controllerName = filterContext.ActionDescriptior.ControllerDescriptor.ControllerName;
if (isAuthenticated && myUserActionPermissionsService.UserCanAccessAction(userName, actionName, contollerName)
{
filterContext.Result = HttpUnauthorizedResult(); // aborts action executing
}
}
}
3.Decorate actions (controllers), that accessible for users granted by admin:
MySpecialController: Controller
{
[UserPermissionRequired]
Action1()
{
//...
}
[UserPermissionRequired]
Action2()
{
//...
}
Action3()
{
//...
}
}
I don't recommend to use base controller for that aim, because attribute usage is more flexible (you have control on action/controller level instead of only controller level), it is better way to implement separated responsibility. Base controller and filter attribute usage correlated as polymorphism and switch operator.
You're asking a very broad question, and it would take some time to review all your requirements. In any case, you could start by adding a user property to a controller from which all other controllers inherit. Then, you could interrogate that user instance to determine whether they have access to the current route. This solution should give you the foundation you need to add some administrative views for your business requirements.
public class MegaController
{
protected User CurrentUser { get; set; }
protected override void Initialize(RequestContext context)
{
if (requestContext.HttpContext.User.Identity.IsAuthenticated)
{
var userRepository = new UserRepository();
CurrentUser = userRepository.GetUser(
requestContext.HttpContext.User.Identity.Name);
}
}
}
The User and UserRepository types can be your own design. You could use LINQ To Entities to wrap a table named "User" and then within your controllers, you could have access to any fields in that table.
Then, subclass all controllers from MegaController
public class AdminController : MegaController
{
public ActionResult Action1()
{
return View();
}
}
public class SomeOtherController : MegaController
{
public ActionResult Action1()
{
return View();
}
}
Now, this doesn't completely solve your "admin" issue. To do so, you could include logic in MegaController.Initialize() to interrogate the request information. Once you have the requested route and user in context, your code could make a decision whether to allow the request, redirect it, etc.
protected override void Initialize(RequestContext context)
{
// ...
if(context.HttpContext != null)
{
if(context.HttpContext.Request.Path == "some/restricted/route"
&& CurrentUser.Role != "Admin")
{
// or similar error page
var url = Url.Action("UnAuthorized", "Error");
context.HttpContext.Response.Redirect(url);
}
}
}
One caveat with this method is that any new controllers added to your application would have to inherit from MegaController, an architecture that could be easily missed by future developers on the project.
Read about plain old forms authentication to add support for roles and user management.
Then use the [Authorize(Roles="RoleName1")] on controllers or actions to control access.
Check MvcMembership, also available on Nuget. You will have all the basics for User Management in an ASP.NET MVC 3 site.
You will need a user / role provider. Read this tutorial to learn how to setup a database that will hold your users and roles. Once it is setup, you will have all the stored procedures needed for first setup / manual testing created.

Categories

Resources