ASP.NET MVC Multi site - where to store site configuration data - asp.net-mvc

I'm developing an ASP.NET MVC multi site application (can host multiple sites from the same application instance / multi-tenant application).
I've previously done this in a web forms application and loaded up the appropriate site configuration data (by inspecting the url) on the page_load event of a custom base page.
With ASP.NET MVC would I be best to do the same in Application_BeginRequest? I can then add the configuration object to HttpContext.Current.Items.

I'm doing something similar with the current system I'm working on.
I'm determining the site by the url the user accesses the application as follows:
public class BaseController : Controller
{
protected override void Initialize(RequestContext requestContext)
{
var host = requestContext.HttpContext.Request.Url.Host;
Site = _siteService.GetSiteByHost(host);
base.Initialize(requestContext);
}
}
Every Controller then extends BaseController, so the Site object is available to every Controller. If you have any more specific questions, ask and I'll let you know what we did.
update
What you see above is _siteService.GetSiteByHost(host). SiteService has a caching layer between it and the Repository, which takes care of all the cache related stuff. it is defined as:
public Site GetSiteByHost(string host)
{
string rawKey = GetCacheKey(string.Format("GetSiteByHost by host{0}", host));
var site = (Site)_cachingService.GetCacheItem(rawKey);
if (site == null)
{
site = _siteRepository.GetSiteByHost(host);
AddCacheItem(rawKey, site);
}
return site;
}
We try not to use the Session unless we're dealing with simple data types, like an integer. I wouldn't store a Site object in the Session, but I would store an integer that represents the SiteId in the session. Then the next time the user accesses the application, I can get the Site object that's associated with the current user from the cache by Id. In the case above though that's not necessary.

This blogger has what looks to be a decent series of articles about multi-tenancy in asp.net-mvc
What you mention sounds like a workable way of doing what you want, though it may not be the idiomatic solution.

Related

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).

MVC Allow anonymous, still get current user when logged in

I have a problem with my MVC 4.0/Razor site.
It's a (not yet launched) public site that I recently inherited.
90% of all pages should be available to everyone, the rest are for superusers and need authentication.
This is handled via an AllowAnonymous attribute on the public facing pages, implemented like this;
public class RequireAuthenticationAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
var skipAuthorization = filterContext.ActionDescriptor.IsDefined(typeof(AllowAnonymousAttribute), true) ||
filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(
typeof(AllowAnonymousAttribute), true);
if (!skipAuthorization)
base.OnAuthorization(filterContext);
}
}
Now, the problem is that I want a few customizations of the public facing sites (for the sake of argument, let's assume a "Currently logged in: XYZ"-label somewhere). What I've tried is using the User.Identity, but on all pages with AllowAnonymous, User.Identity.Name == "", even though a super user did log in. (And if he changes the url to a page with authentication, he's logged in again, and User.Identity.Name is correct).
Is there any way to both use Allow Anonymous and keep track of who's logged in?
I think I have solved this.
The problem was our custom subdomain routing. I had to override the "domain" setting so it points to .example.com instead of example.com for the cookies (mainly aspxauth).
The reason this took some time to realize was a number of other custom made parts, potentially interfering in the application, especially:
Custom membership provider
Custom AllowAnonymous attribute (even when there's a standard attribute now)
And the fact that I thought this was the normal behavior when in fact it's not.
Summary:
If implementing a subdomain routing rule AND need authentication that follows along the subdomains, you must change the base domain of the cookies.

Obtaining the current Principal outside of the Web tier

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.

How would authentication and authorization be implemented using RavenDb in an MVC app?

While I'm well used to using the standard ASP.Net Membership Provider for new MVC web applications, I've been getting a kick out of using RavenDb lately but I still don't believe I have a grasp on the best practice for implementing user authentication and role authorisation.
The code I have replaced my Register and Logon methods with in the AccountController looks like the following:
[HttpPost]
public ActionResult Register(RegisterModel model)
{
if (ModelState.IsValid)
{
using (IDocumentSession Session = DataDocumentStore.Instance.OpenSession())
{
Session.Store(new AuthenticationUser
{
Name = Email,
Id = String.Format("Raven/Users/{0}", Name),
AllowedDatabases = new[] { "*" }
}.SetPassword(Password));
Session.SaveChanges();
FormsAuthentication.SetAuthCookie(model.UserName, createPersistentCookie: false);
// ...etc. etc.
[HttpPost]
public JsonResult JsonLogOn(LogOnModel model, string returnUrl)
{
if (ModelState.IsValid)
{
using (IDocumentSession Session = DataDocumentStore.Instance.OpenSession())
{
book Ok = Session.Load<AuthenticationUser>(String.Format("Raven/Users/{0}", Username)).ValidatePassword(Password);
FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
// etc...
I've seen the RavenDb Membership Provider code that a number of people have referenced in similar posts or questions, but there also seems to be a number of people who consider this to be over the top and leveraging an inefficient API for a data store that doesn't need most of what's provided within it.
So what is the best architectural / design strategy for RavenDb authentication (not for OAuth, but Forms Authentication) and am I barking up the right tree?
I think there are a few parts to this problem. First, from the MVC project's perspective, you really want to use something that will work with the AuthorizationAttribute. This actually does not require using a MembershipProvider per se, but rather stuffing an appropriate IPrincipal into HttpContext.Current.User as that is what those attributes look at to authorize things.
From a HTTP perspective, taking advantage of the existing forms authentication infrastructure also makes a ton of sense -- it solves most of the sticky security issues you really should not solve yourself and it is very flexible in terms of working with what you provide.
From there you get to the gist of the question -- how you want to back your authentication system from a data perspective. I think that is a very tactical call -- some apps it might make sense to just use a MembershipProvider style model. But if I had an app that was very user centric where I was storing lots of user data I would probably consider rolling a custom user store based around my requirements. If you are using the Authentication bundle you could glom onto that to some extent as well. But I don't think there is a hard and fast rule at this point -- do what makes sense for your app.
One thing you should not do is use the AuthenticationUser like above -- that is meant for database system users. In SQL Server terms that would be like making every user in your app a SQL user and then authenticating against that. Which is how some old intranet products used to work but the world has moved past that now.

Subdomains in Asp.Net MVC Application

Helo Guys!
I'll start to develop a website of marketing and the users will be able to create accounts in this website. I'd like to separete the area of users using Subdomains, just like wordpress.com, ning.com, etc. If a user create an account for example: "john", the website should respond in john.mydomain.com for this user area, but it'll be the same code (separating my subdomain).
I'd like to know, How can I do a code to identify the account (in subdomain) to show the correct account for the website ?
PS: Of course it isn't to be authenticated to show the website.
Thanks
My buddy and I get this one of our sites. We had a database table that held the SiteId, along with the Domain.
In a base controller, we parsed the domain when it came in, and then found the appropriate SiteId in the database. All the controllers inherited from this base controller, and the SiteId was passed though to each call to our model.
Admin.Site.com -> SiteId = 1
Frank.Site.com -> SiteId = 2
When someone hit Admin.Site.com, SiteId of 1 was passed to the model to either retrieve the data, or save/edit/add/delete it.
Here is the code we used to get the domain:
public Website CurrentWebsite
{
get
{
if (_website == null)
{
var domain = HttpContext.Request.Url.Host;
_website = _websiteRepository.GetWebsiteByDomain(domain);
}
return _website;
}
}
The route handling in asp.net mvc starts after the domain name so it can not be caught using regular route handling.
You can create a route handler that adds the subdomain into the route data (taking it from the Request.Url) before the route is evaluated.

Resources