Using webapi with formsauthentication (i know may sound weird, but it exactly what I need).
I have a route like :
[Route("{application}/orders}"]
public IHttpActionResult Get(string application) {
var orders = OrderService.GetOrders(application); // return list of orders of applicaiton.
return Ok(orders);
}
Example scenario :
User John has permission to see orders for application "CoolApp"
User Leia has permission to see orders for application "Robin"
user Tristan doesn't have permission to see orders.
On the DB is like a Role - Resource relationship.
UserRole:
User - Role - Application
=====================================
John - OrdersManager - CoolApp
Leia - OrdersManager - Robin
Tristan - OtherRole - Robin
I want that route to check if the user has permission to get orders of that specific application.
I read this: Resource based authorization in .net and looks promising.
First thing is I'm not sure if I should extend using AuthorizeAttribute, AuthorizationFilterAttribute or IAuthorizationFilter as briefly explained here http://www.asp.net/web-api/overview/security/authentication-and-authorization-in-aspnet-web-api unfortunately, is not of much information.
Second I read that binding happens AFTER the authorization so I don't know how to get the values from the RouteData since I have the {application} defined on the route data I guess I should be able to access it. There are few examples for MVC, but was not able to get one for web api and with object like request and context all over the actionContext object I'm not sure where to get it and how in a proper way.
And last but not least, what method of the chosen extension point(s) should I override. If it have both async and sync, should I override both ?
BONUS: Since the authorize will have a dependency on my repository, would be great to know how to inject that into the filter if that's even possible. (I'm not using any IoC container).
Overriding AuthorizationFilterAttribute would probably be the better route. What you'd want to do is pass the attributes you want to check for:
public class MyAuthorizeAttribute : AuthorizeAttribute
{
string role;
string application
public MyAuthorizeAttribute (string role, string application){
this. role = roles;
this.application = application;
}
protected override bool IsAuthorized(HttpActionContext actionContext) {
var routeData = actionContext.ControllerContext.RouteData;
//do your checks
}
}
you can get access to routedata from the HttpActionContext in the IsAuthorized method. Now you can attach the attribute to your ApiController or Action Method.
I'd suggest using an IoC container is you want to do DI with an Web API ActionFilter
Related
I've got a web api that accepts authentication through either an api key, a user access token, or a client access token. I've already written a DelegatingHandler for each case, that creates a new ClaimsPrincipal based on the given authentication details, and have confirmed that the principal is accessible within a controller action.
What I want to do now is inject either a company, a user, or a publisher into the route values, so I can create overloads on the controller for each case. What class/interface do I need to extend in order to plug into the pipeline with the following conditions:
Has access to the current principal
Has access to the route data
The action has not yet been selected
Edit
I'm not looking to sidestep routing here - I still want MVC to choose a route for me, based on the route values. I just want to add one more parameter to the route values before it chooses, and the parameter will have a different name and type depending on whether a user access token is used, or an api key is used.
I believe the subject of authentication should result in two distinct methods, because an api key can access all resources for a company, while a user access token can only access the resources they have been given permission to view.
I do not see a reason why you would want to go with a controller here. You would be sidestepping routing, a very opinionated piece of MVC. I would create middleware that runs before MVC (which is, itself, just middleware) instead.
If you're looking to affect RouteData inline, I would look into using a global IResourceFilter or IAsyncResourceFilter. Then, you can update the RouteData property on the ResourceExecutingContext based upon the conditions you specified in your question.
Any additional dependencies you need to determine how to populate the RouteData property can be injected into the resource filter's constructor as specified in the section on dependency injection.
public class SetRouteValueResourceFilter : IAsyncResourceFilter {
public async Task OnResourceExecutionAsync(ResourceExecutingContext context, ResourceExecutionDelegate next) {
var company = context.HttpContext.Identity.FindFirst("Company")?.Value;
if (company != null) {
context.RouteData.Values.Add("company", company);
}
await next();
}
}
This answer is just an idea. Even though this is handled before the action logic, I'm not sure it will affect which route is selected. According to this diagram, routes are selected before filters are ran.
I managed to get this going by making a custom ValueProviderFactory, which reads values from the current principal's claims and makes them available for parameter binding:
public class ClaimsPrincipalValueProviderFactory : ValueProviderFactory
{
public override IValueProvider GetValueProvider(HttpActionContext actionContext)
{
if (actionContext.RequestContext.Principal != null && actionContext.RequestContext.Principal is ClaimsPrincipal principal)
{
var pairs = principal.Claims.Select(claim => new KeyValuePair<string, string>(claim.Type, claim.Value));
return new NameValuePairsValueProvider(pairs, CultureInfo.CurrentCulture);
}
return null;
}
}
In order to use it, you can annotate the input parameters with the ValueProvider attribute:
public class FooController : ApiController
{
[HttpGet]
public void Bar([ValueProvider(typeof(ClaimsPrincipalValueProviderFactory))]ApiKey apiKey)
{
// ...
}
}
That's pretty ugly and unreadable, what I really wanted was something like the FromUri attribute, but for claims. ValueProviderAttribute and FromUriAttribute both inherit from ModelBinderAttribute, so I created a class which does the same:
/// <summary>
/// The action parameter comes from the user's claims, if the user is authorized.
/// </summary>
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)]
public class FromClaimsAttribute : ModelBinderAttribute
{
public override HttpParameterBinding GetBinding(HttpParameterDescriptor parameter)
{
return parameter.BindWithModelBinding(new ClaimsPrincipalValueProviderFactory());
}
}
Now the example method on the FooController looks a lot more readable:
[HttpGet]
public void Bar([FromClaims]ApiKey apiKey)
{
// ...
}
Update
Looks like this is still having problems with route selection and overloading, especially when some of the parameters are nullable and the value is null. I'm going to have to keep looking into this.
Update #2
I managed to simplify the value provider stuff a lot, after finding there is a built-in NameValuePairValueProvider
Right now I have the following website structure:
Front end: website\user1, website\user2
Back end: website\account\user1, website\account\user2
Where website\account controller has generic [Authorize] attribute applied. Which is not enough since any authorized user can access other user's backed functionality simply by going to website\account\ url (if he knows his name).
What is the best way to resolve this issue?
I have two approaches so far:
Create custom Authorize attribute, inspect controller context, extract user information from there and compare it against current authorized user in ASP.NET:
var currerntUserId = (long)System.Web.Security.Membership.GetUser().ProviderUserKey;
return ExtractCurrentUserId(filterContext) == currerntUserId;
Remove part from back-end URLs and have all users access \account. Current user information will be provided by ASP.NET framework.
How about this?
Route /website/my-account
[Authorize]
public class MyAccountController : Controller
{
public ActionResult Index()
{
var userData = System.Web.Security.Membership.GetUser();
// note you could also get this from db using this.User.Identity.Name
return View(userData);
}
}
It is much easier to control the authorization this way because we are not passing the userid to the action method via a route parameter. The only way someone can get to the backend for a particular user account is by being logged in as that user.
Reply to comments:
To answer your questions in comments about what is easier / harder / better / what my preference is, I am going to go ahead and make my final answer "It depends."
It depends on the sensitivity of the data, what things admins can do that users aren't allowed to (or vice versa), how many controller actions needed to be secured, how similar the views are for public / account / admin perspectives on the data, etc. Pretty much everything stated in your question and all the answers here are valid approaches. You can certainly do it with an ActionFilter and keep the user URL's, or you could do it directly in the action method (if there aren't a lot of them), change your URL schema, implement impersonation (or not), etc.
When you are retrieving a user's data from your datastore (most likely a database), you should only retrieve data for the username of the authenticated user. In your controller, this will give you the username of the currently authenticated user:
User.Identity.Name
So you could do something like:
return ExtractCurrentUserId(filterContext) == User.Identity.Name;
If you use Role based authentication with SimpleMembership you can do something like this and give users roles that should be able to access certain controller actions:
public class MyAccountController : Controller
{
[Authorize(Roles = "Admin")]
public ActionResult User1()
{
// do user1 work
}
[Authorize]
public ActionResult User2()
{
// do user2 work
}
}
I am using Asp.Net WebAPI for a project. I am currently working on authentication and authorization.
I have a messageHandler that will check the HTTP authentication header of a request and build my identity and user profile. However, I want to annotate my controller action (or just the controller) with claims that the action may require (we have a lot of claims that a user can have, so I don't want to load them all).
e.g.:
public class MyController : ApiController
{
[LoadClaims("SomeClaim", "SomeOtherClaim", "etc")]
public string Get()
{
if (HasClaim("SomeClaim"))
return "Awesome";
return "Bummer";
}
}
Inside the authentication message handler I want to be able to look at the attributes and bring claims back from the DB based on only what is required. For that I need to know what Controller and Action I will hit based on route:
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
...
var routeData = request.GetRouteData();
object controllerName;
object actionName;
routeData.Values.TryGetValue("controller", out controllerName);
...
So I can get that. But now I need to turn this into a Type that I can reflect on, but all I have is the controller name (not even the full class name or namespace). How can I turn this into something that I can reflect on to get attributes etc?
I am looking into DefaultHttpControllerSelector to see how the WebAPI stack does it, and it seems to use HttpControllerTypeCache. This is an internal class so I can't create an instance. What is the correct way to go about getting the target controller Type?
You can get access to the type resolver yourself using the global service locator.
var controllerTypeResolver = GlobalConfiguration.Configuration.Services.GetHttpControllerTypeResolver();
var controllerTypes = controllerTypeResolver.GetControllerTypes(GlobalConfiguration.Configuration.Services.GetAssembliesResolver());
var controllerType = controllerTypes.SingleOrDefault(ct => ct.Name == string.Format("{0}Controller", controllerName));
You will probably want to do some caching of the results (like the controller selector does). But this approach should work.
But
You may be better off moving this logic into a Custom authorisation filter that sits on the controller rather than a delegating handler. Given you need to know the controller type you may as well let the ControllerSelector work normally. Perhaps, if you turned your load claims attribute into an authorization action filter attribute you could just load the claims passed in as parameters and set the principal and claims there and then?
If you are still set on the DelegatingHandler you could get the Controller Selector instance itself, which'll be way more efficient:
var controllerSelector = GlobalConfiguration.Configuration.Services.GetHttpControllerSelector();
var controllerDescriptor = controllerSelector.SelectController( request );
I have 3 different types of users (with different roles) interacting on my web application, they all perform some task - some can be exactly the same e.g. create a quote others can be unique to that specific user e.g. sign off quote.
For more clarity 3 types of users: Client, Supplier, Customer.
Client or Customer can create a quote, however only the Customer can sign off a quote.
How do I ensure my application allows clients to access client speficic controllers and suppliers to access supplier specific controllers or areas. Via Custom Attributes? Do I store the type of user inside a cookie? Is this safe? or Session state? As soon as someone logs onto the system I send back a LoggedOnDTO object on which I store Username, UserID, and type of user....
NOTE: I went away from asp.net build in way of creating users, I have my own custom tables with my custom mechanism for logging into the system. I have a registered Model Bindiner that looks for the prefix and I send in a strongly typed object to each action...
Sample code:
[HttpGet]
public ActionResult AddComment(int quoteid, ClientUserDTO loggedonclientuser)
{
}
[HttpGet]
public ActionResult AddCommentSupplier(int quoteid, Supplier loggedonsuppluser)
{
}
EDIT: This method for some reason seems so much simpler... Is there something wrong with it? Any possible security issues? Threading?
My session controller is:
if (_authService.isValidUser(model))
{
var data = _authService.GetAuthenticationCookieDetails(model);
AuthenticateCookie.AddDetailsToCookie(data);
return Redirect(Url.Action("Index", "Activity"));
}
When I create my cookie... I can simple store "ClientUser", "Supplier" or whatever role they are inside the cookie.
Then I can create an Attribute and read in the cookie data to see if they are a valid user e.g.
public class ClientAuthorizationAttribute : AuthorizeAttribute
{
public bool AlwaysAllowLocalRequests = false;
protected override bool AuthorizeCore(System.Web.HttpContextBase httpContext)
{
if (AlwaysAllowLocalRequests && httpContext.Request.IsLocal)
{
bool authorized = false;
var result = UserDetails.GetTypeFromTicket(httpContext.User.Identity as FormsIdentity);
if (result.Equals("client", StringComparison.OrdinalIgnoreCase))
{
authorized = true;
}
//throw no access exception?
return authorized;
}
return base.AuthorizeCore(httpContext);
}
}
Register the attribute under my base controller and I have a simple working solution???
Write a custom MembershipProvider and a Custom RoleProvider then you can decorate your controler class or specific methods with the attribute
<Authorize(Roles:="ROLENAME")>
You can learn how to make that your asp mvc use the custom membershiprovider in this question It's really easy.
Edited:
The way you did it looks right, but I think you take the long way. Implementing your own MembershipProvider and your own Roleprovider will take you no more than 20 minutes... and you will have the benefits of being working with a well tested and documented system and still having the benefits of use your own database tables for the login. In a simple login system, you only have to write two functions in the roleprovider (GetRolesForUser and IsUserInRole) and only one function in the membershipprovider (ValidateUser) and you will get your system working.
If you want, I can put somewhere (maybe pastebin) a well commented versiĆ³n of a membershipProvider as well of a roleprovider that i'm using in a simple app (they're made in vb.net but i'm sure it will not be a problem)
You can also write generic code in the base controller instead of decorating each action with Authorize attribute. Please refer below url.
Good practice to do common authorization in a custom controller factory?
custom-controller-factory/5361846#5361846
I already know about User and Role-based security in ASP.NET MVC. But now I need something a little more granular.
Let's say I have a list of documents, some of which the user is authorized for, some not. Each document has a corresponding record in a documents table in a database. Documents can be downloaded for viewing, if the user has security access. Documents can also be added, if you have the role. Each document has an URL, and each document list has an URL.
I would like to security trim the list so that the user only sees those documents for which he is authorized. But I also need to authenticate the URL requests for these lists and documents, since there is nothing preventing a user from bookmarking a document they no longer have access to, or simply typing the URL into the browser.
Is the built-in role-based security model suitable for this, or do I need to create separate, table-based security? Can I put the security in my repository, so that the returned records are already trimmed, or should it be part of the controller? Do I need a security attribute to validate the controller request, or should I just put it in the controller method as the first few lines of code?
#Robert, I think you've already answered your own question when you said you should trim them (before) they reach the view. So in your Business logic, as a preference over the repository, you might want to do a lamda to trim off the excess so to speak.
Im my opinion I would never return any records to the view that the user wasn't allowed to see. Why increase risk and traffic?
As for the bookmarks I think there you're going to need to do some business logic preventing them from going to the url when access no longer exists.
I thought the controller was simply there to service the data to the page and not to have any logic as such so I'd prefer the business layer approach for this one as it does appear to be a business rule.
That might not be what you had in mind but unless there is a better approach it's the one I would use.
I'll try to explain how I intended to implement this in my project. The requirement is similar as yours: Users have Roles which have Permissions and everything can change from Permission definition, Role's Permission list, and User's Role list etc. So in one moment it's possible that User has access to something and in another, if Administrator alter something, he does not have access.
Before I put some code, I'll answer to your questions.
Do I need to create separate,
table-based security?
-Yes
Can I put the security in my
repository, so that the returned
records are already trimmed, or should
it be part of the controller?
-I think security should be a part of business logic so I would put it somewhere in between controller and repository.
Do I need a security attribute to
validate the controller request?
-In my project, I've put it in attribute, but sometimes i need to access it from controller to, but since that I keep security logic in business layer, I don't think it is a problem.
First attribute is simple attribute that just allows logged users to execute action:
public class LoggedUserFilterAttribute : ActionFilterAttribute
{
public bool Logged { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (!SessionManager.IsUserLogged)
{
filterContext.Result = new RedirectToRouteResult(GetRedirectToNotLoggedRouteValues());
this.Logged = false;
}
else
this.Logged = true;
}
public RouteValueDictionary GetRedirectToNotAuthorizedRouteValues()
{
RouteValueDictionary routeValues = new RouteValueDictionary();
routeValues.Add("action", "NotAuthorized");
routeValues.Add("controller", "Authorization");
return routeValues;
}
public RouteValueDictionary GetRedirectToNotLoggedRouteValues()
{
RouteValueDictionary routeValues = new RouteValueDictionary();
routeValues.Add("action", "NotLogged");
routeValues.Add("controller", "Authorization");
return routeValues;
}
}
and then I have, for example, attribute which allows only SuperUsers to access it:
public class SuperUserFilterAttribute : LoggedUserFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
if (Logged)
{
MyBaseController controller = filterContext.Controller as MyBaseController;
if (controller == null)
throw new Exception("Please use MyBaseController instead of built in Controller");
User loggedUser = controller.Model.UserBO.GetUserByID(SessionManager.LoggedUser.UserID);
if(!loggedUser.IsSuperUser)
{
filterContext.Result = new RedirectToRouteResult(GetRedirectToNotAuthorizedRouteValues());
}
}
}
}
The MyBaseController is class that inherits Controller and has an instance of Model class which represent container for business objects. In controllers action body, if needed I check users rights on current entity and depending on that I return proper view:
[LoggedUserFilter]
public ActionResult LoadSomeEntity(int customerServiceID,int entityID)
{
UserRights userPermissionsView = Model.SecurityBO.GetUsersRightsOnEntity(SessionManager.LoggedUser.UserID, entityID);
if(userPermissionsView.Write)
return View("EditEntity",Model.EntityBO.GetEntityByID(entityID));
if(userPermissionsView.Read)
return View("ViewEntity",Model.EntityBO.GetEntityByID(entityID));
return View("NotAuthorized");
}
p.s. I'm not sure if I can suggest anything to someone that obviously has much more experience that me :), so if I'm spamming, I apologize for that.