mwrock / RequestReduce

Instantly makes your .net website faster by reducing the number and size of requests with almost no effort.
www.requestreduce.org
Apache License 2.0
228 stars 49 forks source link

RR configuration option authorizedUserList doesn't work #212

Closed neozhao closed 12 years ago

neozhao commented 12 years ago

According to the wiki page RequestReduce Configuration options, authorizedUserList should be able to filter users that are not in the white list, and the user name should map to HttpContext.User.Identity.Name. However I found it doesn't work in my project. Here is my configuration in web.config:

<RequestReduce ... authorizedUserList="myuser" />

and definition of the myuser is like:

<authentication mode="Forms">
  <forms name=".myform" loginUrl="Login.aspx" requireSSL="false" cookieless="UseCookies" slidingExpiration="false" protection="None" timeout="480">
    <credentials passwordFormat="Clear">
      <user name="myuser" password="12345678" />
    </credentials>
  </forms>
</authentication>

When I tried to access the dashboard from my local machine, I got the 401 unauthorized response even I already logged in as myuser. I built a test page to display the HttpContext.Current.User.Identity.Name value and it proved that I'm myuser.

Therefore I download the source code of RR (mwrock-RequestReduce-562309a) and found something interesting in the RequestReduceModule.cs file that could cause the 401 problem:

public void Init(HttpApplication context)
{
    context.ReleaseRequestState += (sender, e) => InstallFilter(new HttpContextWrapper(((HttpApplication)sender).Context));
    context.PreSendRequestHeaders += (sender, e) => InstallFilter(new HttpContextWrapper(((HttpApplication)sender).Context));
    context.BeginRequest += (sender, e) => HandleRRContent(new HttpContextWrapper(((HttpApplication)sender).Context));
    context.PostAuthenticateRequest += (sender, e) => HandleRRContent(new HttpContextWrapper(((HttpApplication)sender).Context));
}

HandleRRContent handler is mapped to the BeginRequest event, when the HttpContext.Current.User is still not been set yet according to the ASP.NET Application Life Cycle (http://msdn.microsoft.com/en-us/library/bb470252.aspx).

Then in the HandleRRContent method, the dashboard url /RequestReduceContent/dashboard will be mapped to the DashboardHandler, and it's under the RR folder, so DashboardHandler - ProcessRequest() method will be executed.

In the ProcessRequest() method, the if check won't be able to authorize the current user because it's not been set yet (HttpContext.Current.User.Identity.Name returns an empty string), so the method will set the context.Response to 401.

if ((config.AuthorizedUserList.AllowsAnonymous() || config.AuthorizedUserList.Contains(user)) &&
    ipFilter.IsAuthorizedIpAddress(context))
{
    var transformedDashboard = TransformDashboard(dashboardHtml);
    context.Response.Write(transformedDashboard);
}
else
    context.Response.StatusCode = 401;

After that, the PostAuthenticateRequest event will raise, and call the HandleRRContent handler again. However it's too late that the response code is set to 401 already, and we didn't reset it to 200 when the handler is called again.

That's my understanding and observation from the code, and explains why a logged in user will be blocked for the RR dashboard. Please let me know your thoughts.

Thanks! Neo

neozhao commented 12 years ago

I also have a fix locally for this issue, basically we can just stop the HandleRRContent handler calls the DashboardHandler - ProcessRequest() method for the BeginRequest event, because we know it would definitely fail. Then we can process the dashboard successfully when the PostAuthenticateRequest event raise.

mwrock commented 12 years ago

You are absolutely right. Good catch and Thanks!! This is a regression bug from my refactoring of the RequestReduceModule that breaks out the logic into Handlers. I have a fix for this that will be deployed in the next hour or so hopefully.