Obtaining the current Principal outside of the Web tier - asp.net-mvc

I have the following ntier app: MVC > Services > Repository > Domain. I am using Forms authentication. Is it safe to use Thread.CurrentPrincipal outside of my MVC layer to get the currently logged in user of my application or should I be using HttpContext.Current.User?
The reason I ask is there seems to be some issues around Thread.CurrentPrincipal, but I am cautious to add a reference to System.Web outside of my MVC layer in case I need to provide a non web font end in the future.
Update
I have been following the advice recieved so far to pass the username into the Service as part of the params to the method being called and this has lead to a refinement of my original question. I need to be able to check if the user is in a particular role in a number of my Service and Domain methods. There seems to be a couple of solutions to this, just wondering which is the best way to proceed:
Pass the whole HttpContext.Current.User as a param instead of just the username.
Call Thread.CurrentPrincipal outside of my web tier and use that. But how do I ensure it is equal to HttpContext.Current.User?
Stick to passing in the username as suggested so far and then use Roles.IsUserInRole. The problem with this approach is that it requires a ref to System.Web which I feel is not correct outside of my MVC layer.
How would you suggest I proceed?

I wouldn't do either, HttpContext.Current.User is specific to your web layer.
Why not inject the username into your service layer?

Map the relevant User details to a new Class to represent the LoggedInUser and pass that as an argument to your Business layer method
public class LoggedInUser
{
public string UserName { set;get;}
//other relevant proerties
}
Now set the values of this and pass to your BL method
var usr=new LoggedInUser();
usr.UserName="test value "; //Read from the FormsAuthentication stuff and Set
var result=YourBusinessLayerClass.SomeOperation(usr);

You should abstract your user information so that it doesn't depend on Thread.CurrentPrincipal or HttpContext.Current.User.
You could add a constructor or method parameter that accepts a user name, for example.
Here's an overly simplified example of a constructor parameter:
class YourBusinessClass
{
string _userName;
public YourBusinessClass(string userName)
{
_userName = userName;
}
public void SomeBusinessMethodThatNeedsUserName()
{
if (_userName == "sally")
{
// do something for sally
}
}
}

I prefer option number 2( use Thread.CurrentPrincipal outside of web tier ). since this will not polute your service tier & data tier methods. with bonuses: you can store your roles + additional info in the custom principal;
To make sure Thread.CurrentPrincipal in your service and data tier is the same as your web tier; you can set your HttpContext.Current.User (Context.User) in Global.asax(Application_AuthenticateRequest). Other alternative location where you can set this are added at the bottom.
sample code:
//sample synchronizing HttpContext.Current.User with Thread.CurrentPrincipal
protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
//make sure principal is not set for anonymous user/unauthenticated request
if (authCookie != null && Request.IsAuthenticated)
{
FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
//your additional info stored in cookies: multiple roles, privileges, etc
string userData = authTicket.UserData;
CustomPrincipal userPrincipal = PrincipalHelper.CreatePrincipal(authTicket.Name, authTicket.UserData, Request.IsAuthenticated);
Context.User = userPrincipal;
}
}
of course first you must implement your login form to create authorization cookies containing your custom principal.
Application_AuthenticateRequest will be executed for any request to server(css files, javascript files, images files etc). To limit this functionality only to controller action, you can try setting the custom principal in ActionFilter(I haven't tried this). What I have tried is setting this functionality inside an Interceptor for Controllers(I use Castle Windsor for my Dependency Injection and Aspect Oriented Programming).

I believe you are running into this problem because you need to limit your domains responsibility further. It should not be the responsibility of your service or your document to handle authorization. That responsibility should be handled by your MVC layer, as the current user is logged in to your web app, not your domain.
If, instead of trying to look up the current user from your service, or document, you perform the check in your MVC app, you get something like this:
if(Roles.IsUserInRole("DocumentEditorRole")){
//UpdateDocument does NOT authorize the user. It does only 1 thing, update the document.
myDocumentService.UpdateDocument(currentUsername, documentToEdit);
} else {
lblPermissionDenied.InnerText = #"You do not have permission
to edit this document.";
}
which is clean, easy to read, and allows you to keep your services and domain classes free from authorization concerns. You can still map Roles.IsUserInRole("DocumentEditorRole")to your viewmodel, so the only this you are losing, is the CurrentUserCanEdit method on your Document class. But if you think of your domain model as representing real world objects, that method doesn't belong on Document anyway. You might think of it as a method on a domain User object (user.CanEditDocument(doc)), but all in all, I think you will be happier if you keep your authorization out of your domain layer.

Related

Security Trimming MVC Sitemap Provider Nodes With AuthAttribute Based on Route Values

We have a fully working sitemap with many hundreds of nodes configured with sitemap attributes on the actions. These nodes are are security trimmed working perfectly based on claims. All working great and fast.
We now have a requirement that certain pages are inaccessible based on a route value. Basically hiding mvc menu links based on the value of personId in the route. Using the code below:
//Just proof of concept - people restricted will be from a service
public class PersonAuthorizeAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (!httpContext.Request.RequestContext.RouteData.Values.ContainsKey("personId"))
{
return base.AuthorizeCore(httpContext);
}
var personId = httpContext.Request.RequestContext.RouteData.Values["personId"].ToString();
int value;
int.TryParse(personId, out value);
if (value != 0 && value == 3708)
{
return false;
}
return base.AuthorizeCore(httpContext);
}
}
This works perfectly in terms of preventing access. However unlike the rest of our security trimming sitemap doesn't work with this. If I visit the person that is restricted first it hides the node for that person and everyone else. If I visit a non hidden person then the node is visible for everyone but I get a denied access request for the person if I try to visit their node.
I assume it is related to the fact that a node doesn't have a concept of trimming based on route values.
Any ideas how this could be implemented. We are trying to implement a more flexible security model.
There is a disconnect here because of the difference between the way MVC and MvcSiteMapProvider uses the AuthorizeAttribute.
In MVC, the AuthorizeAttribute is simply checked against the context of the current request. The current context contains everything that is needed to determine whether the user is authorized to view the current page.
MvcSiteMapProvider checks every node for each request. Therefore, we can't make the same assumptions that the current context is correct for determining that a node is accessible. There is a new temporary HttpContext created for each node (based on the node's generated URL) and the route values used during the check are derived from that context (not the current context).
This fake HttpContext is not perfect. Microsoft has created several redundant properties HttpContext, RequestContext, etc. that must be explicitly set or they will default to the current context, and not all of them have been set by the AuthorizeAttributeAclModule. This was done intentionally in this case because we want the current request (current user) to be checked when it comes to security.
As a result, your check is always using the route values from the current HttpContext, not the fake context that is created based on the node's URL. To use the fake context, you need to override OnAuthorization and use the RequestContext there.
public override void OnAuthorization(AuthorizationContext filterContext)
{
var personId = filterContext.RequestContext.RouteData.Values["personId"];
base.OnAuthorization(filterContext);
}
The real issue here is that (if you are using preserved route parameters) you are basing your security for the entire site on a route value which only applies to the currrent request. What is supposed to happen when your request changes to something that doesn't include the personId? Should the nodes all be invisible? Should they all be visible? What if the user changes the route value by changing the URL manually? There are a lot of cracks in this security model.
Important: Microsoft has said time and again the importance of not basing MVC security on the URL. The route values are just an abstraction of the URL, but don't really change the fact that they are based on the URL. It is virtually impossible to guarantee that an action method will only have a single route that can access it, which is why AuthorizeAttribute was created. AuthorizeAttribute secures the resource (action method) at its source so it can't be defeated by these alternate routes. Basing it on routes entirely defeats its purpose. In short, by including a route value in AuthorizeAttribute, you are opening up the possibility that your application can be hacked by an unintended alternate route to the action method. You are not simplifying security by basing AuthorizeAttribute on routes, you are making it more complex and nearly impossible to completely control over time.
It would be better to base your security on the IPrincipal and IIdentity interfaces that are part of every form of security that plugs into MVC. In this particular case, there is a user property that is already supported by the AuthorizeAttribute. There is no built-in way to add a not user, but that functionality could easily be added to your custom AuthorizeAttribute.

Web Api Security client and user

pretty new to creating Web APIs, I am currently trying to secure my API and have a couple of questions
So basically I have a Web API and an MVC app. The API currently has a controller called Account that has two methods Register and Login. The MVC app has the same controller with actions but just calls the api methods.
Now basically they way I see it, I only ever want my MVC app to use the Web API, so ill have an api key in the MVC app webconfig that gets passed to the API each time? Also users need to authenticate so at the same time passing the user details?
Will this mean I need to setup two AuthAttributes? One for a user and one for the api details?
EDIT
To take this example a bit further and to demonstrate what I need
I have an WebUI that has a controller called CreateTeam. This passes a Team model up to the api Controller method CreateTeam, the api method requires that the user is authorized to create a team. Now this works fine but....
I also have a controller on my api called LeaguesController, which has a method AddNewTeamsToLeagues. Now I have a console app that runs every hour that calls this method on the api to add new teams to leagues. Now I dont ever want anyone to call this method on the api, I only ever want the console app to be able to use it. Whats the best way to secure this?
One solution is to use the token generated by [AntiForgeryValidation] (the Razor helper is #Html.AntiForgeryToken). You can use the following token (generated on your MVC View) to assist with validation if you'd like (it can be very helpful) or use your own:
<input name="__RequestVerificationToken" type="hidden" value="some-generated-value" />
If you're using jQuery you can override the default Ajax options (jQuery.ajaxSetup() - API documentation) to automatically add this to your request headers and validate against it (in whatever implementation you want). You can also obviously send in a username and whatever else you'd like for verification uses.
Then, you can have your Web API have a filter that validates against this information (and whatever else you'd like) using AntiForgery.Validate(cookieValue, request.Headers["__RequestVerificationToken"]);.
Unfortunately, until MVC6/Web API3 the two controller types have completely different implementation so you may have to write a customer filter yourself to handle authentication. There are dedicated [Authorize] attributes for both MVC and Web API but they have have different namespaces (System.Web.Http.AuthorizeAttribute vs System.Web.Mvc.AuthorizeAttribute).
Hope this helps. For further reading, you can check out this blog post.
-- Edit to reply to your updated comment about your Console application --
You could always create a Filter that only allows local connections, specific IP addresses, certain LDAP/AD Security Groups, etc to have access to a specific controller/controller action. In your case of a Console application you would need to decide how you'd want that to be secured; once you decide to you can go from there. So, say you want to allow only specific members of an AD Security Group to access the controller, you could throw together a filter like so:
namespace YourAppName.Filters
{
public class AuthorizeADAttribute : AuthorizeAttribute
{
public string Groups { get; set; }
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (base.AuthorizeCore(httpContext))
{
var groups = Groups.Split(',').ToList();
var context = new PrincipalContext(ContextType.Domain, "YourDomainHere");
var userPrincipal = UserPrincipal.FindByIdentity(
context,
IdentityType.SamAccountName,
httpContext.User.Identity.Name);
foreach (var group in groups)
if (userPrincipal.IsMemberOf(context,
IdentityType.Name,
group))
return true;
}
return false;
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if (filterContext.HttpContext.User.Identity.IsAuthenticated)
{
var result = new ViewResult();
result.ViewName = "NotAuthorized";
result.MasterName = "_Layout";
filterContext.Result = result;
}
else
base.HandleUnauthorizedRequest(filterContext);
}
}
}
And then apply it to a controller or a method inside of your controller, like so:
[AuthorizeAD(Groups = "SecurityGroupToAuth, League Admins, Console App Group")]
public YourViewModelHere AddNewTeamsToLeagues()
{
// do stuff
}
So, to answer your initial question: you'll likely need two different attributes/filters for the different types (between handling the AntiforgeryToken and then the console app). Unfortunately without knowing how your application and console application are hosted (different machines, same subnet, on the same network, etc) I can't give much more information but hopefully this helps point you in the right direction for creating your own filter/attribute(s).

Supply user specific dependencies to MVC controller with Ninject

I have some controllers that require a web service connection (an instance of MS Dynamics CRM CrmService) and I would like the controllers to receive this through their constructors. The CRM service has to be set up with a token that is based on who the currently logged in user is (when the user logs in the application authenticates against the CRM and can store the returned token in Session).
I'm not sure how best to supply this instance using Dependency Injection and Ninject. It seems a bit rubbish for the Ninject ToMethod() Func<> to access FormsAuth/Session for the current request (to obtain the token if authenticated) to create the appropriate instance. I'm also not sure what should happen if the user is not authenticated - I don't need these users be able to access the controller but the controller will be instantiated before any filters like [Authorize] will run so the dependency will always have to be met. From what I have read returning null is not ideal and I would have to change the Ninject configuration to do this anyway.
I was thinking that maybe the controller could get an instance of ICrmServiceFactory or something but that doesn't help me if the controllers end up having other dependencies which also rely on CrmService directly (and don't want to be passed a factory).
Any advice on how to solve this would be appreciated.
I usually set up a binding for IPrincipal:
kernel.Bind<IPrincipal>().ToMethod(c => HttpContext.Current.User);
Never really had a problem with this approach.
If I understand your question correctly then your controller has a dependency to CrmService and the CrmService requires some token stored in the session.
In that case you could inject a CrmTokenProvider to CrmService and add a property to that class which gets the value from the session whenever it is requested by the CrmService.
public class CrmService
{
public CrmService(CrmTokenProvider tokenProvider)
{
this.tokenProvider = tokenProvider;
}
public void DoSomeWork()
{
...
this.tokenProvider.Token;
...
}
}
I have ended up implementing this as follows:
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<CrmService>()
.ToMethod(context =>
{
//return unauthenticated instance if user not logged in.
if (!HttpContext.Current.User.Identity.IsAuthenticated) return new CrmService();
return GetConnection(HttpContext.Current);
})
.InRequestScope();
}
private static CrmService GetConnection(HttpContext ctx)
{
//get stuff out of session and return service
}

ASP.NET MVC custom IPrincipal injection

I'm working on an application using ASP.NET MVC 1.0 and I'm trying to inject a custom IPrincipal object in to the HttpContext.Current.User object.
With a traditional WebForms application I've used the Application_AuthenticateRequest event to do this as follows.
protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
if (HttpContext.Current.User != null)
{
if (HttpContext.Current.User.Identity.IsAuthenticated)
{
if (HttpContext.Current.User.Identity is FormsIdentity)
{
// Get Forms Identity From Current User
FormsIdentity id = (FormsIdentity)HttpContext.Current.User.Identity;
// Get Forms Ticket From Identity object
FormsAuthenticationTicket ticket = id.Ticket;
// Create a new Generic Principal Instance and assign to Current User
SiteUser siteUser = new SiteUser(Convert.ToInt32(id.Name));
HttpContext.Current.User = siteUser;
}
}
}
}
So using this I was able to access my custom IPrincipal by either explicitly casting the User object to type SiteUser. I actually did this by having a custom class that all Pages were inheriting from which did this under the covers for me.
Anyhow, my problem is that with ASP.NET MVC the Application_AuthenticateRequest seems to fire whenever any request is made (so for JS files, images etc.) which causes the application to die.
Any help or suggestions as to how I can go about injecting my custom IPrincipal in to the HttpContext.Current.User object within ASP.NET MVC 1.0 would be greatly appreciated. I did see the following post on SO, but it didn't seem to cater for what I'm trying to achieve: ASP.NET MVC - Set custom IIdentity or IPrincipal
TIA.
my problem is that with ASP.NET MVC
the Application_AuthenticateRequest
seems to fire whenever any request is
made (so for JS files, images etc.)
which causes the application to die.
This isn't an uniquely MVC problem - if you ran your application on IIS7 with the integrated pipeline in place then you would see the same thing.
If the problem with the lookup is scalability then I assume the actual problem is within
FormsAuthenticationTicket ticket = id.Ticket;
SiteUser siteUser = new SiteUser(Convert.ToInt32(id.Name));
I'd guess that your SiteUser class does some sort of database lookup. If you examine how forms auth works the ticket contains all the information necessary to produce a FormsIdentity (this doesn't hold true for roles, unless you specifically enable roles caching to a cookie). So you ought to look at the same approach. The first time you construct your siteUser object cache it within a signed cookie, then use the cookie to rehydrate your SiteUser properties on subsequent requests.
If you do this then you can go one step further, replacing the Thread principle with your SiteUser, or at least a custom IPrincipal/IUser combination which has the same information as your SiteUser class would have.
So inside AuthenticateRequest you'd have some flow like
SiteUserSecurityToken sessionToken = null;
if (TryReadSiteUserSecurityToken(ref sessionToken) && sessionToken != null)
{
// Call functions to attach my principal.
}
else
{
if (HttpContext.Current.User != null &&
HttpContext.Current.User.Identity.IsAuthenticated &&
HttpContext.Current.User.Identity is FormsIdentity)
{
// Get my SiteUser object
// Create SiteUserSecurityToken
// Call functions to attach my principal.
}
}
And the function to attach the principal would contain something like
HttpContext.Current.User = sessionSecurityToken.ClaimsPrincipal;
Thread.CurrentPrincipal = sessionSecurityToken.ClaimsPrincipal;
this.ContextSessionSecurityToken = sessionSecurityToken;
You'll want to make sure that the functions which write the Security Token to a cookie add, at a minimum, a checksum/MAC value, and, if you like, support encryption using the machine key if it's configured to do so. The read functions should validate these values.
This sounds like a job for a custom Authorization Filter.

asp.net mvc authentication against shibboleth and authorization

Where do I get information about the currently connected user? That is, how does shibboleth pass the information?
Can I set some restrictions on actions using [Authorize] attribute based on data acquired from shibboleth?
Shibboleth publishes user attributes associated with
sessions into HTTP request headers, based on header names defined
in Attribute Acceptance Policy (1.3.x) or Attribute Mapping (2.x)
files. These headers are transformed into CGI variables based
on mapping rules defined by the CGI specification.
You should be aware of this security advisory:
http://shibboleth.net/community/advisories/secadv_20090615.txt
I have never user shibboleth, but you can get information about the user from Controller.User property. It will return a generic principal of current thread. Using this principal you can check whether the user is authenticated and get a login name of the user. This is due to the reason that after logon an authentication cookie is set and this cookie contains limited amount of information. And on each request after logon only this cookie is checked (if it exists and valid - user is authenticated).
So if you need in some specific information you can manually load a user (it's better to use cache here) and check whatever you want.
Also you can create and attach your own principal with necessary information to the thread on start of a request (e.g. on start of a request load the user from db/cache using user name from base principal, create and set your own principal to thread). After this you can check all properties of the user you need.
Where would you attach your own principal? You say on the start of the request but what if you don't want every request authorizing?
You'll want to create a method in Global.asax.cs that has the following signature
protected void Application_PostAuthenticateRequest()
{
//Your code here.
}
This will be called automatically before almost anything else is done (MVC will call this method if it exists, you don't have to "turn it on" anywhere), and this is where you need to set the Principal. For instance, let's assume you have a header called RolesHeader that has a comma separated value of roles and another header called UserId that has (duh) the user ID.
Your code, without any error handling, might look something like:
protected void Application_PostAuthenticateRequest()
{
var rolesheader = Context.Request.Headers["RolesHeader"];
var userId = Context.Request.Headers["UserId"];
var roles = rolesheader.Split(',');
var principal = new GenericPrincipal(new GenericIdentity(userId), roles);
Context.User = principal;
}
It's the Principal/Identity that the [Authorize] attribute uses, so setting it here at the beginning of the request lifecycle means the [Authorize] attribute will work correctly.
The rest of this is optional, but I recommend it:
I like to create my own custom classes that implement IPrincipal and IIdentity instead of using the GenericPrincipal and GenericIdentity, so I can stuff more user information in it. My custom Principal and Identity objects then have much more rich information, such as branch numbers or email addresses or whatever.
Then, I create a Controller called BaseController that has the following
protected new CustomPrincipal User
{
get
{
return (base.User as CustomPrincipal) ?? CustomPrincipal.GetUnauthorizedPrincipal();
}
}
This allows me to access all my rich, custom Principal data instead of just what's defined in IPrincipal. All of my real controllers then inherit from BaseController instead of directly from Controller.
Obviously, when using a custom Principal like this, in the Application_PostAuthenticateRequest() method, you'd set the Context.User to be your CustomPrincipal instead of a GenericPrincipal.

Resources