views:

1040

answers:

1

I am building a web application using ASP.NET MVC that has two very distinct types of users. I'll contrive an example and say that one type is content producers (publishers) and another is content consumers (subscribers).

I am not planning on using the built-in ASP.NET authorization stuff, because the separation of my user types is a dichotomy, you're either a publisher or a subscriber, not both. So, the build-in authorization is more complex than I need. Plus I am planning on using MySQL.

I was thinking about storing them in the same table with an enum field (technically an int field). Then creating a CustomAuthorizationAttribute, where I'd pass in the userType needed for that page.

For example, the PublishContent page would require userType == UserType.Publisher, so only Publishers could access it. So, creating this attribute gives me access to the HttpContextBase, which contains the standard User field (of type IPrincipal). How do I get my UserType field onto this IPrincipal? So, then my attribute would look like:

public class PublisherAuthorizationAttribute : AuthorizeAttribute
{
    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        if (!httpContext.User.Identity.IsAuthenticated)
            return false;

        if (!httpContext.User.Identity.UserType == UserTypes.Publisher)
            return false;

        return true;
    }
}

Or does anyone think my entire approach is flawed?

A: 

I would still use the built in ASP.NET forms authentication but just customize it to your needs.

So you'll need to get your User class to implement the IPrincipal interface and then write your own custom cookie handling. Then you can simply just use the built in [Authorize] attribute.

Currently I have something similar to the following...

In my global.asax

protected void Application_AuthenticateRequest()
{
    HttpCookie cookie = Request.Cookies.Get(FormsAuthentication.FormsCookieName);
    if (cookie == null)
        return;

    bool isPersistent;
    int webuserid = GetUserId(cookie, out isPersistent);

    //Lets see if the user exists
    var webUserRepository = Kernel.Get<IWebUserRepository>();

    try
    {
        WebUser current = webUserRepository.GetById(webuserid);

        //Refresh the cookie
        var formsAuth = Kernel.Get<IFormsAuthService>();

        Response.Cookies.Add(formsAuth.GetAuthCookie(current, isPersistent));
        Context.User = current;
    }
    catch (Exception ex)
    {
        //TODO: Logging
        RemoveAuthCookieAndRedirectToDefaultPage();
    }
}

private int GetUserId(HttpCookie cookie, out bool isPersistent)
{
    try
    {
        FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(cookie.Value);
        isPersistent = ticket.IsPersistent;
        return int.Parse(ticket.UserData);
    }
    catch (Exception ex)
    {
        //TODO: Logging

        RemoveAuthCookieAndRedirectToDefaultPage();
        isPersistent = false;
        return -1;
    }
}

AccountController.cs

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult LogOn(LogOnForm logOnForm)
{
    try
    {
        if (ModelState.IsValid)
        {
            WebUser user = AccountService.GetWebUserFromLogOnForm(logOnForm);

            Response.Cookies.Add(FormsAuth.GetAuthCookie(user, logOnForm.RememberMe));

            return Redirect(logOnForm.ReturnUrl);
        }
    }
    catch (ServiceLayerException ex)
    {
        ex.BindToModelState(ModelState);
    }
    catch
    {
        ModelState.AddModelError("*", "There was server error trying to log on, try again. If your problem persists, please contact us.");
    }

    return View("LogOn", logOnForm);
}

And finally my FormsAuthService:

public HttpCookie GetAuthCookie(WebUser webUser, bool createPersistentCookie)
{
    var ticket = new FormsAuthenticationTicket(1,
                                               webUser.Email,
                                               DateTime.Now,
                                               DateTime.Now.AddMonths(1),
                                               createPersistentCookie,
                                               webUser.Id.ToString());

    string cookieValue = FormsAuthentication.Encrypt(ticket);

    var authCookie = new HttpCookie(FormsAuthentication.FormsCookieName, cookieValue)
                         {
                             Path = "/"
                         };

    if (createPersistentCookie)
        authCookie.Expires = ticket.Expiration;

    return authCookie;
}

HTHs
Charles

Charlino
This would make for an untestable mess in MVC. Definitely not the right approach.
Jeff Putz
Untestable mess? Hardly. That's extremely short sighted to say. Both the `AccountService` and the `FormsAuth` service are interfaces that are injected into the `AccountController` - making that totally testable. Everything else is dependent on FormsAuthentication (how else do you do formsauth?) which, as you should know, is notoriously hard to test - as the default ASP.NET MVC project points out. Sure you could extract the FormsAuthentication calls out to a service but what's is that really going to achieve considering where the calls to it are?
Charlino