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
Related
I have create a new controller, inherited from the Umbraco.Web.Mvc.UmbracoAuthorizedController and trying to limit it to only logged in Umbraco Administrators.
My current solution displays the view for only logged in umbraco users, but I cannot filter for only admins.
Code:
I have a Composer and I set up the route config:
public class ApplicationEventComposer : IComposer
{
public void Compose(Composition composition)
{
RouteTable.Routes.MapRoute(
name: "ITTest",
url: "umbraco/backoffice/ITTest/{action}/{id}",
defaults: new { controller = "ITTest", action = "Index", id = UrlParameter.Optional }
);
composition.Register<ITTestController>(Lifetime.Request);
}
}
I have a controller:
public class ITTestController : Umbraco.Web.Mvc.UmbracoAuthorizedController
{
public ActionResult Index()
{
return View("/Views/ITTest/Index.cshtml");
}
}
I have tried to add different attributes to filter for only adminsitrators like:
[UmbracoAuthorize(Roles = "admin")]
[UmbracoApplicationAuthorize(Roles = "admin")]
[AdminUsersAuthorize]
And tried different roles like "admin", "administrator", "administrators", "Administrators" etc. but nothing seems to work.
(Side note: At the moment I am thinking about a workaround and overwrite the OnAuthorization event, but that would be more of a hack than a proper solution.)
Questions:
How can I filter the users using Umbraco roles?
What are the role names exactly? Are they the user group names or something else?
Update:
(I tried to improve the answer below, but it was rejected, so I will add my findings here)
The [Authorize(Roles = "admin")] one is working!
I was playing around with it. To make it work it still needs to be under "umbraco/backoffice", but it does not have to be a UmbracoAuthorizedController it seems to be working fine when it is (only) RenderMvcController
The built in role names are:
"admin"
"sensitiveData"
"translator"
"writer"
"editor"
For more info: https://our.umbraco.com/forum/using-umbraco-and-getting-started/99651-limit-umbracoauthorizedcontroller-to-umbraco-admin-users-only#comment-313527
The UmbracoAuthorizedController controller effectively just adds the UmbracoAuthorize attribute to your controller, but it seems this attribute ignores any roles you pass in, and just checks the visitor is an authenticated back-office user.
You can see this in detail in the AuthorizeCore method in:
https://github.com/umbraco/Umbraco-CMS/blob/853087a75044b814df458457dc9a1f778cc89749/src/Umbraco.Web/Mvc/UmbracoAuthorizeAttribute.cs
and the ValidateRequestAttempt method in:
https://github.com/umbraco/Umbraco-CMS/blob/853087a75044b814df458457dc9a1f778cc89749/src/Umbraco.Web/Security/WebSecurity.cs
This isn't what I would have expected!
To achieve what you require you could inherit from the Umbraco.Web.Mvc.UmbracoController controller and decorate it with a standard MVC Authorize attribute.
I've successfully tested the following in Umbraco 8.2.0:
public class ITTestController : Umbraco.Web.Mvc.UmbracoController
{
[Authorize(Roles = "someGroup")]
public ActionResult Index()
{
return View("/Views/ITTest/Index.cshtml");
}
}
where someGroup is the Umbraco group you wish to allow.
Umbraco documentation:
https://our.umbraco.com/documentation/Implementation/Controllers/
suggests you should use the Umbraco.Web.Mvc.UmbracoAuthorizeAttribute attribute, which I think you've tried, so I would just update the Roles to be Roles = "Administrators", so you have something like:
[UmbracoAuthorize(Roles = "Administrators")]
public ActionResult Index()
{
return View("/Views/ITTest/Index.cshtml");
}
I have an existing API that has No Authentication. It`s a public Web API which several clients use by making simple requests.
Now, there is the need to authorize access to a certain method.
Is there any way to do this, keeping the rest of the controllers and respective methods "open" for the clients that already use this Web API?
How can i identify if the request has permissions to access this "protected" method?
What you'll need to do is add an [Authorize] attribute to the methods you want to protect optionally using the overload that accepts one or more role names that the calling user must be in.
Then what you'll have to implement is a way to ensure that authentication data of the caller is transformed into a Principal object. Setting the Principal is generally something you don't do yourself, but instead have the framework do for you.
If you do want to provide your own interface, you can using an authentication filter implementing the System.Web.Http.Filters.IAuthenticationFilter interface.
So what you'll get is this:
[MyAuthentication]
[Authorize]
public SomeClass MyProtectedMethod() {
return new SomeClass();
}
And then implement the MyAuthentication attribute. Below is an example, the important thing is that you use the context of the incoming request and end up setting the context.Principal property with a new Principal
public class MyAuthentication : ActionFilterAttribute, System.Web.Http.Filters.IAuthenticationFilter {
public async Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
{
// 1. Look for credentials in the request.
HttpRequestMessage request = context.Request;
AuthenticationHeaderValue authorization = request.Headers.Authorization;
// 2. If there are no credentials, do nothing.
if (authorization == null)
{
return;
}
// 3. If there are credentials but the filter does not recognize the
// authentication scheme, do nothing.
if (authorization.Scheme != "Basic")
{
return;
}
// 4. If there are credentials that the filter understands, try to validate them.
// 5. If the credentials are bad, set the error result.
if (String.IsNullOrEmpty(authorization.Parameter))
{
context.ErrorResult = new AuthenticationFailureResult("Missing credentials", request);
return;
}
Tuple<string, string> userNameAndPasword = ExtractUserNameAndPassword(authorization.Parameter);
if (userNameAndPasword == null)
{
context.ErrorResult = new AuthenticationFailureResult("Invalid credentials", request);
}
string userName = userNameAndPasword.Item1;
string password = userNameAndPasword.Item2;
IPrincipal principal = await AuthenticateAsync(userName, password, cancellationToken);
if (principal == null)
{
context.ErrorResult = new AuthenticationFailureResult("Invalid username or password", request);
}
// 6. If the credentials are valid, set principal.
else
{
context.Principal = principal;
}
}
... other interface methods here
}
I hope this helps you get on the right track. For more information check this post:
http://www.asp.net/web-api/overview/security/authentication-filters
You can use [Authorize] attribute at particular API method as well as at controller level. In case you put the [Authorize] attribute at controller level then you can use [AllowAnonymous] attribute for those API method which you want to access without authentication.
By default, authorization is globally disabled on application. You can force your controller to only allow authorized requests by adding the action filter [Authorize].
[Authorize] // This will enforce all methods inside should be authorized
public class AuthorizeController : ApiController
{
//this method will only be called if user is authorized
public IHttpActionResult GetList()
{
return Ok();
}
}
You can also force only certain methods be authorized:
public class AuthorizeController : ApiController
{
[Authorize] //this method will only be called if user is authorized
public IHttpActionResult GetList()
{
return Ok();
}
// This method can still be called even if user is not authorized
public IHttpActionResult GetListUnauthorized()
{
return Ok();
}
}
Or just disable authorization on some methods inside a controller that requires authorization:
[Authorize]
public class AuthorizeController : ApiController
{
//this method will only be called if user is authorized
public IHttpActionResult GetList()
{
return Ok();
}
[AllowAnonymous]// This method can be called even if user is not authorized due the AllowAnonymous attribute
public IHttpActionResult GetListUnauthorized()
{
return Ok();
}
}
You can also set who's allowed to access your method by using:
[Authorize(Users="Joey,Billy")]
Or by Rules using:
[Authorize(Roles="Administrator,Manager")]
Or even build a more complex Authorize attribute like in this answer (Based on Claims): Authorization Attribute by Claims
We solved it using [AllowAnonymous] on the method, who we didn't want to be Authenticated but Authorizated, overriding the Authorization.
The Execution flow will goes to method level then its goes to Controller Level. So if you mention as "AllowAnonymous" will execute with out Authorization check.
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.
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")]
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.