views:

40

answers:

1

Some background to my issue:

It appears that there is a change/bug in Mvc2 concerning ValidateAntiForgeryTokenAttribute.

When upgrading from Mvc1 to Mvc2, users with an active session will receive the following error when they request a page using ValidateAntiForgeryTokenAttribute:

Unable to cast object of type 'System.Web.UI.Triplet' to type 'System.Object[]'.

The issue is documented here.

After upgrading to Mvc2, we anticipate that we will be seriously affected by this issue. I have coded a fix derived from the code in the comments (documented below for posterity). At the moment this code is invoked by creating a subclass of Controller and AsyncController overriding the Initialize method in order to correct the problem. e.g.

public class FixedController:Controller
{
    protected override void Initialize(RequestContext requestContext)
    {
        base.Initialize(requestContext);
        this.FixAntiForgeryTokenMvc1ToMvc2(requestContext); //extension
    }
}

internal static class ControllerEx
{
    public static void FixAntiForgeryTokenMvc1ToMvc2(
        this Controller controller,
        RequestContext requestContext)
    {
        var cc = new ControllerContext(requestContext,
                                       controller);
        var antiForgeryAttribute = new ValidateAntiForgeryTokenAttribute();
        try
        {
            antiForgeryAttribute.OnAuthorization(new AuthorizationContext(cc));
        }
        catch (HttpAntiForgeryException forgeryException)
        {
            var castException = forgeryException.InnerException;
            if (castException != null
                && castException is InvalidCastException
                && castException.Message.StartsWith(
                       "Unable to cast object of type"
                       + " 'System.Web.UI.Triplet' to type"
                       + " 'System.Object[]'"))
            {
                var responseTokenCookieNames =
                    controller
                        .Response
                        .Cookies
                        .Cast<Cookie>()
                        .Select(c => c.Name)
                        .Where(n => n.Contains("RequestVerificationToken"));
                foreach (var s in responseTokenCookieNames)
                {
                    var cookie = controller.Response.Cookies[s];
                    if (cookie != null)
                    {
                        cookie.Value = "";
                    }
                }
                var requestTokenCookieNames =
                    controller
                        .Request
                        .Cookies
                        .Cast<String>()
                        .Where(n => n.Contains("RequestVerificationToken"))
                        .ToList();
                foreach (var c in requestTokenCookieNames)
                {
                    controller.Request.Cookies.Remove(c);
                }
            }
        }
    }
}

The knock on effect of this is that I must alter all my controller classes to derive from my new, corrected Controller subclasses. This seems quite intrusive for code that intend to deprecate in about a month's time.

So, coming to my question, I'd like to know if there is a less intrusive means of patching existing classes such that downstream users of the class do not have to be altered, perhaps using reflection?

+1  A: 

Spender,

rather than code every controller, use a basecontroller and inherit from that along the lines of:

public abstract class BaseController : Controller
{
    protected override void Initialize(RequestContext requestContext)
    {
        base.Initialize(requestContext);
        FixAntiForgeryTokenMvc1ToMvc2(this, requestContext);
    }
    private static void FixAntiForgeryTokenMvc1ToMvc2(
        Controller controller, RequestContext requestContext)
    {
        // stuff ....
    }
}

and in your 'normal' controllers, just have:

public class NormalController : BaseController
{
    // all the previous stuff
}

give it a go...

jim
That's the current way that I am doing it. It still requires changing all controllers to derive from the new base.
spender
ok - in that case, wouldn't the single change to your base controller trickle down to everything that's inherited from it??. of course, i'm assuming that you've 'always' had a base controller in place, hence my casual suggestion. otherwise, i guess the jury's still out then :(
jim
Sure... that's exactly the plan, so as such things are pretty nicely under control. I was really wondering on a general level if theres a less intrusive method of patching bad code. Until somebody says different, I'm fairly happy I've taken the best approach.
spender
spender, imho, i think you've taken the most widely understood approach and thus prolly the most pragmatic for what is after all a transitory situation.
jim