3

I am building a site with a mixture of public and member-only pages. The login system works fine as it is.

However I'd like to launch a closed, invite-only preview and temporarily require visitors log in for all actions except the welcome page.

Currently I have [Authorize] attributes on certain action methods. I could add additional attributes to the other action methods too, but this feels a bit hacky and will make it harder to remove them correctly later when I want to revert to having more open content.

So, how can I effectively add a site-wide authorisation requirement? Recall that I want the single /Home/Index route to remain public as well, so that people I've invited can read some information before activating their invites.

Drew Noakes
  • 300,895
  • 165
  • 679
  • 742
  • Perhaps put the welcome page in a different controller from the protected pages. I think there is some way to require authorization for a whole controller. – CodesInChaos Jan 18 '11 at 21:25
  • @CodeInChaos, yes that quite simple. I have about 20 controllers, but that'd still take less time that writing the above question :) I believe it's also possible to register a filter in my HttpApplication class to run before _every_ request, so I could probably subclass the authorize attribute and short circuit if it was my welcome page. I am quite new to the MVC framework though and would like to know what the best practice for this kind of thing is. – Drew Noakes Jan 18 '11 at 21:38
  • 1
    I have a scenario similar to yours, and had great success with Steve Willcock's solution http://stackoverflow.com/questions/746998/override-authorize-attribute-in-asp-net-mvc/747208#747208 I also think having all your controllers inherit from a base controller is a good idea. – Daniel Liuzzi Feb 02 '11 at 20:44

3 Answers3

3

I think doing this in the web.config file would be the best since it is temporary and will not require that you add any C# code or rely on roles/names etc.

Do the following in your web.config

<configuration>
      <!-- system.web is the only already in your web.config 
           don't add this, just add the authorization element
           to the existing system.web element -->
  <system.web>
     <authorization>
        <deny users="?"/>
     </authorization>
  </system.web>

  <!-- the login path -->
  <location path="Login/Index">
     <system.web>
        <authorization>
           <allow users="?"/>
        </authorization>
     </system.web>
  </location>

  <!-- welcome page -->
  <location path="Home/Index">
     <system.web>
        <authorization>
           <allow users="?"/>
        </authorization>
     </system.web>
  </location>

  <!-- static files (images, css, js etc.) folder -->
  <location path="Content">
     <system.web>
        <authorization>
           <allow users="?"/>
        </authorization>
     </system.web>
  </location>

</configuration>

The first <authorization> element will restrict access to your application to only authenticated/logged in users. The subsequent <location> elements place exceptions on specific paths (login page, welcome screen and static files).

Make sure you verify that the path attribute is correct for each location based on your application.

Some more details on the location element can be found at MSDN 'location element'.

Omar
  • 39,496
  • 45
  • 145
  • 213
  • thanks for this. I like the idea of doing this through config, but unfortunately the above doesn't work for me. It complains that the `` section is repeated, quoting a line within the `` element as a duplicate. I've tried a few versions of this but haven't gotten it to work just yet. – Drew Noakes Jan 18 '11 at 22:11
  • Hmmm... that's caused when you the `path` attribute is set to nothing. What you can do is put the welcome information on the same page where the user logins in. This will cause unauthorized requests be redirected to the login page. – Omar Jan 18 '11 at 22:18
  • I'd rather not change my login page. And I had to set the path attribute to nothing as my `Home/Index` route maps to `mysite.com/` (root). Thanks anyway! – Drew Noakes Jan 18 '11 at 22:21
  • This works great when you are using Windows Authentication in a LAN. You can even have the default section to be locked down, and then provide greater access to a specific location. Also remember, if you change your route tables you'll have to come back and check that the paths were not affected. – Lessan Vaezi Jul 11 '11 at 11:22
2

Add a custom filter - here's my implementation for a similar requirement. The main difference from what you describe is that it requires a role set from the invite page rather than letting in any logged in user.

public class PreviewAuthAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        base.OnActionExecuting(filterContext);

        // if site is live, show page 
        if (Data.Settings.IsLive) return;

        // if request is from localhost or build server, show page
        if (filterContext.HttpContext.Request.IsLocal) return;
        if (filterContext.HttpContext.Request.UserHostAddress.StartsWith("192.168.0")) return;

        // if user has has alpha or admin role, show page
        if (filterContext.HttpContext.Request.IsAuthenticated && (filterContext.HttpContext.User.IsInRole("Alpha") || filterContext.HttpContext.User.IsInRole("Admin"))) return;

        // site is not live and user does not have access - show placeholder

        filterContext.Result = new ViewResult()
        {                
            ViewName="Placeholder",
            ViewData = filterContext.Controller.ViewData,
            TempData = filterContext.Controller.TempData
        };
    }

}
Tom Clarkson
  • 16,074
  • 2
  • 43
  • 51
  • This looks interesting. Can I apply this attribute to the entire site, or do I need to bake it into each controller individually? – Drew Noakes Jan 18 '11 at 22:22
  • I think you can set it up globally with MVC3, though I haven't tried that yet. If you set it up that way, you will also have to add code to exclude the login page from the filtering. Part of my intention with this design was to have something that would not get confused with the regular authorization (which can still be tested by connecting from a known ip) and would effectively remove itself when the site goes live so that it doesn't matter if the attributes are scattered through the code. – Tom Clarkson Jan 18 '11 at 22:31
1

You could customize the Authorize attribute and have your list of users for private beta need to pass some logic within. Just remove the customization when you go public.

Stack Overflow answer on how to customize the Authorize attribute

Look at the answer containing AuthorizeOwnerAttribute too.

Community
  • 1
  • 1
Greg Levenhagen
  • 924
  • 7
  • 14