i have webapi action which is decorated with customauthattribute for authorization. This attribute internally checks with db if current user has viewcustomer permissions. Does anyone know better way of handling it instead of using customattribute. may be intercepting somewhere all request and run authorization checks for user/permisson/resource he is trying to access : eg getcustomer for customer id 10. So if user doesnt have access see customer id 10 he should get 403 status.
[CheckPermission(Process.CustomerManagment,Permissions.View)]
public IHttpActionResult GetCustomer(int customerId)
{
}
You can add global filters in the configuration of your web api.
Practically in your startup class (startup.cs or webapi.config) you can call on the httpconfiguration object the following method
var config = new HttpConfiguration();
config.Filters.Add(new MyAuthFilterAttribute());
In this way it will be global for all your api calls.
You should extend the IAuthenticationFilter interface.
take a look here for documentation
webapi documentation
One option would be to create a filter that you can apply globally. For example, something like this. Yes it's horrible but gives you a start:
public class GlobalAuthoriseAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
var controllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName;
var actionName = filterContext.ActionDescriptor.ActionName;
switch (controllerName)
{
case "Home":
//All call to home controller are allowed
return;
case "Admin":
filterContext.Result = new HttpUnauthorizedResult();
return;
}
}
}
Now you can add this to your entire app in the App_Start\FilterConfig.cs file:
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new GlobalAuthoriseAttribute());
}
Related
In all of my asp.net mvc projects up to this point, permissions to controllers are set by using a custom class based on the [Authorize] attribute.
However, what if I wanted an administrator role who could grant access to views instead of going through the trouble of having to touch the controller to add/remove roles, re-complile, and push the changes to production. How would I go about doing this?
as discussed. Try the below.
in the controller as I'm sure you're aware.
[PermissionsFilter("CanAccessMyView")]
public ActionResult ReturnMyView ()
{
//etc..
}
Then, in your custom class
public class PermissionsFilter : AuthorizeAttribute
{
private readonly PermissionManager _permissionsManager;
public PermissionsFilter(string permissionName)
{
_permissionName = permissionName;
_permissionsManager = new PermissionServiceManager();
}
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (!_permissionServiceManager.CanAccessPermission(_permissionName))
{
var urlHelper = new UrlHelper(filterContext.RequestContext);
var url = urlHelper.Action("Unauthorised", "Home");
filterContext.Result = new RedirectResult(url);
}
}
}
Where the permissions manager is querying the database or perhaps session info to see if the user user has access.
Hope that helps.
I am very confused with Authentication and Authorization in ASP.NET MVC 5.
I am working on an existing website and I need to add security in it. By security I mean Authentication (Logins) and Authorization (Roles). I have access to a Webservice, but not directly to the database though I can access the Entities (Users, Roles etc.).
Membership Provider seems to be a bit old, so I took a look at Identity but it looks complicated to implement to an existing project, especially when I don't have direct access to the database.
What would be a good solution ? What are the best practices ?
Could you suggest me any good resource so I can suits my needs ?
Thank you.
In case someone feel as lost as I was, here is a potential solution using Claims. Ath the end, you will know how to handle Authentication, Authorization and Roles.
Hope this can help.
Startup config
In the root folder off my project I have created a file, startup.cs. She contains a partial class that we will use to configure the application to use a cookie that store the signed user.
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
}
}
Then, in the App_Start I have a file, Startup.Auth.cs
public partial class Startup
{
public void ConfigureAuth(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login")
});
}
}
Controller
First, I have created an AcountController.cs with attribute of type IAuthenticationManager. This attribute gets the authentication middleware functionality available on the current request.
public class CompteController : Controller
{
private IAuthenticationManager AuthenticationManager
{
get
{
return HttpContext.GetOwinContext().Authentication;
}
}
}
Then, I have a classic view called Login with GET and POST. In the post I check in my Webservice if the user can Log In. If he can, I call a the magic function to authenticate. In this code, the class User is the custom User I get in the Webservice. He don't implement IUser.
private void AuthentifyUser(User user, bool isPersistent)
{
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
CustomIdentity identity = new CustomIdentity(user);
CustomPrincipal principal = new CustomPrincipal(identity);
Thread.CurrentPrincipal = principal;
AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity);
}
Last important method in my Controller allow users to Log Out.
public ActionResult Deconnexion()
{
AuthenticationManager.SignOut();
return RedirectToAction("Login", "Account");
}
Claims
CustomIdentity and CustomPrincipal are two custom class that I use for the Claims system. They indirectly implement IIdentity and IPrincipal. I put them in a separate new folder.
-Remember, A principal object represents the security context of the user on whose behalf the code is running, including that user's identity (IIdentity) and any roles to which they belong.
-An identity object represents the user on whose behalf the code is running.
public class HosteamIdentity : ClaimsIdentity
{
public HosteamIdentity(User user)
: base(DefaultAuthenticationTypes.ApplicationCookie)
{
AddClaim(new Claim("IdUser", user.Id.ToString()));
AddClaim(new Claim(ClaimTypes.Name, user.Name));
AddClaim(new Claim(ClaimTypes.Role, user.Role));
}
public int IdUser
{
get
{
return Convert.ToInt32(FindFirst("IdUser").Value);
}
}
//Other Getters to facilitate acces to the Claims.
}
The Principal gives us access to the Identity.
public class HosteamPrincipal : ClaimsPrincipal
{
private readonly HosteamIdentity _identity;
public new HosteamIdentity Identity
{
get { return _identity; }
}
public HosteamPrincipal(HosteamIdentity identity)
{
_identity = identity;
}
public override bool IsInRole(string role)
{
return _identity.Role == role;
}
}
Access the CustomPrincipal
Now, I will lgo to the gGlobal.asax, here we will override the Application_PostAuthenticateRequest event. This event is fired when a security module has established the identity of the user.
We will use Thread.CurrentPrincipal, this static object Gets or sets the thread's current principal (for role-based security), so it is perfectly adapted to our case !
You may have to adapt the code here. I personally have to request my Webservice, this may not be your case.
Just talking briefly about our constructors. The fist is empty, we will use it when we don't care about Roles
protected void Application_PostAuthenticateRequest(Object sender, EventArgs e)
{
if (HttpContext.Current.User.Identity.IsAuthenticated)
{
Thread.CurrentPrincipal = new HosteamPrincipal(
new HosteamIdentity(
WebService.GetUser(
HttpContext.Current.User.Identity.Name)));
}
}
In most case, retrieving the user by is Name is not a good practice. Please, adapt the above code to your solution.
Authorize Attribute Filter
Now, it will be great if we could easily tell which Controller or Action can be accessed by an authenticated user. To do so, we will use Filters.
Filters are custom classes that provide both a declarative and programmatic means to add pre-action and post-action behavior to controller action methods. We use them as annotation, for example [Authorize] is a Filter.
As there is to many things to explain, I will let you read the comments, they are very explicit.
Just talking briefly about our Constructors.
-The first one is empty, we will use it when we don't care about Roles. We access it by writing the annotation [CustomAuthorize] abose a Controller or an Action.
-The second one, takes an array of Roles, we will use it by writing the annotation [CustomAuthorize("Role1", "Role2", etc.)] abose a Controller or an Action. He will define which Roles access the Controller or action
public class CustomAuthorize : AuthorizeAttribute
{
private new string[] Roles { get; set; }
public CustomAuthorize() { }
public CustomAuthorize(params string[] roles)
{
this.Roles = roles[0].Split(',');
}
/// <summary>
/// Check for Authorizations (Authenticated, Roles etc.)
/// </summary>
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (httpContext.Request.IsAuthenticated)
if (Roles != null)
{
foreach (string role in Roles)
if (((HosteamPrincipal)Thread.CurrentPrincipal).IsInRole(role))
return true;
return false;
}
else
return true;
return false;
}
/// <summary>
/// Defines actions to do when Authorizations are given or declined
/// </summary>
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (!AuthorizeCore(filterContext.HttpContext))
HandleUnauthorizedRequest(filterContext);
}
/// <summary>
/// Manage when an Authorization is declined
/// </summary>
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if (filterContext.HttpContext.Request.IsAuthenticated)
filterContext.Result = new HttpStatusCodeResult(HttpStatusCode.Forbidden);
else
base.HandleUnauthorizedRequest(filterContext);
}
}
In previous MVC projects using forms authentication i was able to strip the authentication cookie from the response using an action filter with the following override.
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
filterContext.HttpContext.Response.Cookies.Remove(FormsAuthentication.FormsCookieName);
}
I have now switched over to use OWIN based asp.net identity 2.0 and in the same way I would like to remove their version of the authentication cookie.
I have modified the filter (below) to use the new cookie name but the cookie is no longer being removed.
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
const string authenticationCookie = CookieAuthenticationDefaults.CookiePrefix + DefaultAuthenticationTypes.ApplicationCookie;
filterContext.HttpContext.Response.Cookies.Remove(authenticationCookie);
}
Does anyone know why?
The reason is that the authentication occurs in the OWIN pipeline with it's own environment dictionary, whereas your previous FormsAuthentication was using System.Web.HttpContext.
If you set a breakpoint on :
filterContext.HttpContext.Response.Cookies
and view the variable you will see it doesn't even have a cookie named .AspNet.ApplicationCookie so you're not removing anything.
I'm not sure what you're trying to achieve by removing the cookie rather than just logging the user out but a way to do something similar would be to create an action filter as below:
public class CookieStripperAttribute : ActionFilterAttribute {
public override void OnResultExecuted(ResultExecutedContext filterContext) {
filterContext.HttpContext.GetOwinContext().Environment.Add("StripAspCookie", true);
}
}
Apply that as necessary and then do a check for the action prior to OWIN writing out the message headers by creating some OWIN middleware
public class AuthenticationMiddleware : OwinMiddleware
{
const string _authenticationCookie = CookieAuthenticationDefaults.CookiePrefix + DefaultAuthenticationTypes.ApplicationCookie;
public AuthenticationMiddleware(OwinMiddleware next) :
base(next) { }
public override async Task Invoke(IOwinContext context)
{
var response = context.Response;
response.OnSendingHeaders(state =>
{
var resp = (OwinResponse)state;
if (resp.Environment.ContainsKey("StripAspCookie"))
{
resp.Cookies.Delete(_authenticationCookie);
}
}, response);
await Next.Invoke(context);
}
}
You would attach that middleware in your startup class:
app.Use(typeof(AuthenticationMiddleware));
Note though that while this will eat the cookie it wont log the user out but as I say I'm not sure if that is your intention anyway.
Our repositories and services are currently being injected into our controllers via a Unity container (using the Web API MVC bootstrapper).
public class AnnouncementsController : BaseApiController
{
protected IAnnouncementRepository AnnouncementRepository{ get; set; }
public AnnouncementsController (IAnnouncementRepository announcementRepository)
: base()
{
this.AnnouncementRepository = announcementRepository;
}
public HttpResponseMessage Post([FromBody]GetAnnouncementsModel model)
{
var announcements = AnnouncementRepository.GetByType(model.AnnouncementType);
// ...
}
}
A new requirement has arisen: All input models (e.g. GetAnnouncementsModel) now need to have an AccessToken.
Why? So that results from repositories are filtered according to data rights. Clients are restriction on what data they can consume.
Bad Solution - Pass in token as a method parameter
One solution is to include an AccessToken parameter to every repository or service call. This is not a good solution. We have to implement this in hundreds of methods. An example of this parameter:
public HttpResponseMessage Post([FromBody]GetAnnouncementsModel model)
{
var announcements = AnnouncementRepository.GetByType(model.AccessToken, model.AnnouncementType);
// ...
}
Better Solution - Inject token during resolution
A better solution would be to provide the AccessToken in the repository constructors and have some base implementation that does the filtering logic implicitly.
But how could I do this with dependency injection? The constructor is resolved and called by the Unity container. How could I inject the property value of an input model into this process?
container.RegisterType<IAnnouncementRepository, AnnouncementRepository>(
new InjectionConstructor(
new InjectionParameter<Guid>(AccessToken)
)
);
You can define a custom interface, call it for example IAccessTokenProvider:
interface IAccessTokenProvider
{
Guid Token { get; }
}
Now you can make an implementation like this:
public class HttpContextAccessTokenProvider
{
public Guid Token
{
get { return (Guid)HttpContext.Current.Items["AccessToken"]; }
}
public static void SetToken(Guid token)
{
HttpContext.Current.Items["AccessToken"] = token;
}
}
Now you should be able to implement a filter to read the token from the request:
public class TokenFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
string tokenString = filterContext.HttpContext.Request.QueryString["token"];
ActionExecutingContext.SetToken(Guid.Parse(tokenString));
}
}
You can also read the token from other sources or store it in other containers (sessions, cookies, whatever). You can also directly access it in your controller or repositories.
You have 2 options to use the token in your repository:
Inject IAccessTokenProvider to your repository and get the token directly from the provider.
Inject IAccessTokenProvider to your controller and pass the token
I am looking to set the result action from a failed IAuthorizationFilter. However I am unsure how to create an ActionResult from inside the Filter. The controller doesn't seem to be accible from inside the filter so my usual View("SomeView") isn't working. Is there a way to get the controler or else another way of creating an actionresult as it doesn't appear to be instantiable?
Doesn't work:
[AttributeUsage(AttributeTargets.Method)]
public sealed class RequiresAuthenticationAttribute : ActionFilterAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext context)
{
if (!context.HttpContext.User.Identity.IsAuthenticated)
{
context.Result = View("User/Login");
}
}
}
You should look at the implementation of IAuthorizationFilter that comes with the MVC framework, AuthorizeAttribute. If you are using forms authentication, there's no need for you to set the result to User/Login. You can raise a 401 HTTP status response and ASP.NET Will redirect to the login page for you.
The one issue with setting the result to user/login is that the user's address bar is not updated, so they will be on the login page, but the URL won't match. For some people, this is not an issue. But some people want their site's URL to correspond to what the user sees in their browser.
You can instantiate the appropriate ActionResult directly, then set it on the context. For example:
public void OnAuthorization(AuthorizationContext context)
{
if (!context.HttpContext.User.Identity.IsAuthenticated)
{
context.Result = new ViewResult { ViewName = "Whatever" };
}
}