Implementing more advanced authorization rules for web-based ELMAH error log

450 views
Skip to first unread message

Scott Mitchell

unread,
Aug 3, 2009, 1:59:11 PM8/3/09
to ELMAH
Is there any guidance on how to implement more advanced authorization
rules for the web-based ELMAH error log? In short, I want to be able
to display the ELMAH error log to a certain subset of users, but that
subset is defined by business rules in the application and not by user
or role.

I've tried one approach, which was to:

* Disable web viewing for elmah.axd
* Create an ASP.NET page named ViewLog.aspx
* Add authorization rules in Page_Load. If these rules passed then
I did a Server.Transfer to elmah.axd


While the above certainly shows the log to only those authorized users
(whilst prohibiting access via ~/elmah.axd), there are two issues:

(1) The CSS doesn't come cleanly through. I got it looking pretty
good by examining the markup/CSS rendered by elmah.axd and copying and
pasting what I could into ViewLog.aspx, but it's still not perfect.
(2) Whenever I view the error log from ViewLog.aspx I see the
error log, but an exception occurs. I apologize as I can't tell you
what exception is firing, exactly, as the code is at a client's site,
but it happens everytime the page is loaded (either by visiting it
directly or by having the page auto-refresh). (If needed, I can
recreate a sample environment and let you know.) The error doesn't
impede viewing the error log, but it does appear in the log.


Is there a way to achieve what I'm after? If not, is there any
interest in adding support for such to a future version of ELMAH?

Thanks

Atif Aziz

unread,
Aug 3, 2009, 2:57:58 PM8/3/09
to el...@googlegroups.com
Hi Scott,

There is a little known feature in ELMAH that allows custom authorization but unfortunately which has not been documented so far. It's very simple to use, however. All you need to do is add a regular ASP.NET HTTP module that implements the IRequestAuthorizationHandler interface from the Elmah namespace. The interface is dead simple and contains only a single method:

public interface IRequestAuthorizationHandler {
  bool Authorize(HttpContext context);
}

You can find this definition starting line 212 of ErrorLogPageFactory.cs of release 1.0. The Authorize method is called everytime any request takes place via the ErrorLogPageFactoryhandler so it can be used to secure the error log page, details, RSS feeds, style sheet or what have you and using any complexity of rules you desire. Returning true from Authorize will naturally authorize the request and otherwise forbid it. Also note that if you authorize a request, all other security still applies. That is, if you've not enabled remote viewing by configuration then authorizing by your rules won't make the page accessible.

Another thing to keep in mind is that if you want your ELMAH-specific authorization module to work in medium trust then you should inherit from HttpModuleBase and override SupportDiscoverability to return true.

The reason this bit has not been advertized is that it hasn't received as much design scrutiny and field feedback as I would've like but all the machinery is there nonetheless and modular enough to develop and distribute stand-alone authorization modules for ELMAH. If you want to take a crack at it, I'd be happy to guide you through any bits that still may seem foggy. And if it works out well in your case, it'd be great if you can contribute a wiki page surrounding this subject. ;)

- Atif

Scott Mitchell

unread,
Aug 4, 2009, 12:21:52 PM8/4/09
to ELMAH
Atif, thank you for the information.

Next time I meet with my client (this week, I suspect), I'll bring up
this issue. We are currently in the midst of development, so this
isn't a pressing issue, but clearly this is something that will need
to be implemented before going live with the site, which we hope to do
by year's end at the absolute latest. So long story short - the info
is very helpful and will be put to use eventually. When it does I'd be
happy to provide feedback, comments, suggestions, problems, etc.

Thanks again.



On Aug 3, 11:57 am, Atif Aziz <aziza...@gmail.com> wrote:
> Hi Scott,
>
> There is a little known feature in ELMAH that allows custom authorization
> but unfortunately which has not been documented so far. It's very simple to
> use, however. All you need to do is add a regular ASP.NET HTTP module that
> implements the IRequestAuthorizationHandler interface from the
> Elmahnamespace. The interface is dead simple and contains only a
> single method:
>
> public interface IRequestAuthorizationHandler {
>
> >   bool Authorize(HttpContext context);
> > }
>
> You can find this definition starting line 212 of ErrorLogPageFactory.cs of
> release 1.0<http://code.google.com/p/elmah/source/browse/tags/REL-1.0/src/Elmah/E...>.
> The Authorize method is called everytime any request takes place via the
> ErrorLogPageFactoryhandler so it can be used to secure the error log page,
> details, RSS feeds, style sheet or what have you and using any complexity of
> rules you desire. Returning true from Authorize will naturally authorize the
> request and otherwise forbid it. Also note that if you authorize a request,
> all other security still applies. That is, if you've not enabled remote
> viewing by configuration then authorizing by your rules won't make the page
> accessible.
>
> Another thing to keep in mind is that if you want your ELMAH-specific
> authorization module to work in medium trust then you should inherit from
> HttpModuleBase<http://code.google.com/p/elmah/source/browse/tags/REL-1.0/src/Elmah/H...>and
> override
> SupportDiscoverability<http://code.google.com/p/elmah/source/browse/tags/REL-1.0/src/Elmah/H...>to
> return
> true.
>
> The reason this bit has not been advertized is that it hasn't received as
> much design scrutiny and field feedback as I would've like but all the
> machinery is there nonetheless and modular enough to develop and distribute
> stand-alone authorization modules for ELMAH. If you want to take a crack at
> it, I'd be happy to guide you through any bits that still may seem foggy.
> And if it works out well in your case, it'd be great if you can contribute a
> wiki page surrounding this subject. ;)
>
> - Atif
>
> On Mon, Aug 3, 2009 at 7:59 PM, Scott Mitchell
> <scott.k.mitch...@gmail.com>wrote:

Scott Mitchell

unread,
Aug 14, 2009, 2:02:48 PM8/14/09
to ELMAH
Atif, earlier this week I was at the client's site and was trying to
implement this approach. I created an HTTP Module that extended
HttpModuleBase and implemented IRequestAuthorizationHandler's
Authorize method. However, I noticed that when the Authorize method is
invoked that HttpContext.Current.Session is always null. I assume this
has to do with when in the pipeline this method is called. Do you have
any proposed workaround?

The issue is that we are storing credentials in a session variable, so
I cannot perform any sort of authorization check because I don't know
who is visiting the page! :-) This app has its roots in a classic ASP
app and still has a good chunk of its core functionality in classic
ASP, which is part of the reason the credentials are stored in
session. But long story short is that changing this approach is not
feasible at this point in the game. I was hoping there might be some
suggestion you'd have as to how to get this going.

My guess is that the Authorize method is being called during the
MapRequestHandler stage of the HTTP pipeline, but that session isn't
enabled until the AcquireRequestState stage, which happens later.

Thanks



On Aug 3, 11:57 am, Atif Aziz <aziza...@gmail.com> wrote:
> Hi Scott,
>
> There is a little known feature in ELMAH that allows custom authorization
> but unfortunately which has not been documented so far. It's very simple to
> use, however. All you need to do is add a regular ASP.NET HTTP module that
> implements the IRequestAuthorizationHandler interface from the
> Elmahnamespace. The interface is dead simple and contains only a
> single method:
>
> public interface IRequestAuthorizationHandler {
>
> >   bool Authorize(HttpContext context);
> > }
>
> You can find this definition starting line 212 of ErrorLogPageFactory.cs of
> release 1.0<http://code.google.com/p/elmah/source/browse/tags/REL-1.0/src/Elmah/E...>.
> The Authorize method is called everytime any request takes place via the
> ErrorLogPageFactoryhandler so it can be used to secure the error log page,
> details, RSS feeds, style sheet or what have you and using any complexity of
> rules you desire. Returning true from Authorize will naturally authorize the
> request and otherwise forbid it. Also note that if you authorize a request,
> all other security still applies. That is, if you've not enabled remote
> viewing by configuration then authorizing by your rules won't make the page
> accessible.
>
> Another thing to keep in mind is that if you want your ELMAH-specific
> authorization module to work in medium trust then you should inherit from
> SupportDiscoverability<http://code.google.com/p/elmah/source/browse/tags/REL-1.0/src/Elmah/H...>to
> return
> true.
>
> The reason this bit has not been advertized is that it hasn't received as
> much design scrutiny and field feedback as I would've like but all the
> machinery is there nonetheless and modular enough to develop and distribute
> stand-alone authorization modules for ELMAH. If you want to take a crack at
> it, I'd be happy to guide you through any bits that still may seem foggy.
> And if it works out well in your case, it'd be great if you can contribute a
> wiki page surrounding this subject. ;)
>
> - Atif
>
> On Mon, Aug 3, 2009 at 7:59 PM, Scott Mitchell
> <scott.k.mitch...@gmail.com>wrote:

Atif Aziz

unread,
Aug 20, 2009, 7:04:04 PM8/20/09
to el...@googlegroups.com
Scott, thanks a lot for your detailed feedback. I'll try out an idea or two over the weekend based on your observation so far with respect to availability of the session state during authorization. If you don't hear back next week, don't hesitate to poke. :)
 
- Atif

Atif Aziz

unread,
Aug 29, 2009, 1:36:42 PM8/29/09
to el...@googlegroups.com
Hi Scott,
 
I investigated this further and it looks like you're right. The session state is not available at the time IRequestAuthorizationHandler's Authorize method is called. I guess this is to be expected. ELMAH's authorization occurs right before it returns one of its request handlers back to ASP.NET and as you know, ASP.NET relies on the handler indicating sessions state requirements via IRequiresSessionState and IReadOnlySessionState. It's a bit of a catch 22 scenario. The good news is that I have managed to hack a workaround that doesn't require any changes to ELMAH 1.1 and attached is a working example built on top of the demo supplied with ELMAH. Following is an explanation of the solution that requires 3 classes in all (see the App_Code directory).
 
There are really two reasons why session state is not available during authorization. One, and as we discovered, it's just too plain early. Two, none of the ELMAH handlers use session state so they don't implement IRequiresSessionState or IReadOnlySessionState. Even if IRequestAuthorizationHandler.Authorize wasn't being called too early, the session state would have never been made available by ASP.NET because the handlers don't advertise their need for it. Consequently, the first thing that needed fixing was implementing those interfaces on the handlers. Unfortunately, this would mean forking ELMAH's sources and maintaining a modified version. To avoid that, the solution takes a wrapper approach. The first class is pretty straightforward and generic:
 
public sealed class SessionRequringHandler :
    IHttpHandler,
    IRequiresSessionState
{
    public IHttpHandler InnerHandler { get; private set; }
    public SessionRequringHandler(IHttpHandler handler) {
        InnerHandler = handler;
    }
    public void ProcessRequest(HttpContext context) {
        InnerHandler.ProcessRequest(context);
    }
    public bool IsReusable {
        get { return InnerHandler.IsReusable; }
    }
}
 
It takes any IHttpHandler during construction and delegates to it. Because this class advertises session-requirement, it can cause session state to be summoned during a request when the actual handler wouldn't have. Now on to the next class, which is also dead straightforward:
 
public class ErrorLogHandlerFactory :
    Elmah.ErrorLogPageFactory
{
    public override IHttpHandler GetHandler(
        HttpContext context,
        string requestType,
        string url, string pathTranslated)
    {
        var handler = base.GetHandler(context, requestType, url, pathTranslated);
        return new SessionRequringHandler(handler);
    }
 
This is a handler factory that inherits from ELMAH's handler factory and I've named it the same. It first calls the base implementation and then takes the returned handler and wraps it up in an instance of SessionRequiringHandler. Then instead of returning the original handler, the latter one is returned and which will cause ASP.NET to make session state available. To make this work, you'll need to register the new handler factory in lieu of the original one from EMAH; that is, for elmah.axd (or whatever path you've chosen instead) in the httpHandlers section in web.config.
 
The final piece of the puzzle is the authorization handler itself and here's the implementation:
 
public class ElmahAuthorizationHandler :
    HttpModuleBase,
    IRequestAuthorizationHandler
{
    protected override void OnInit(HttpApplication application) {
        application.PreRequestHandlerExecute += delegate {
            var context = application.Context;
            if (IsFlaggedRequest(context))
                CompleteAuthorization(context);
        };
        base.OnInit(application);
    }
    protected override bool SupportDiscoverability {
        get { return true; }
    }
    public virtual bool Authorize(HttpContext context) {
        FlagRequest(context);
        return true;
    }
    protected virtual void CompleteAuthorization(HttpContext context) {
        // Forbid more than 100 requests within a given session.
        if (context.Session == null)
            throw new Exception("Session unavailable.");
        var count = (int)(context.Session["ElmahRequestCount"] ?? 0) + 1;
        if (count > 100)
            throw new HttpException(403, "Unauthorized request.");
        context.Session["ElmahRequestCount"] = count;
    }
    protected virtual void FlagRequest(HttpContext context)
        context.Items[GetType()] = true;
    }
    protected virtual bool IsFlaggedRequest(HttpContext context)
        return (bool)(context.Items[GetType()] ?? false);
    }
}

 
What the handler basically does is that it defers the decision to authorize. When the Authorize method is called, it merely flags the request as one requiring an authorization check later in the processing and tells ELMAH to let it go through for now as if it is authorized. When the call returns, ELMAH returns a handler for the request. This handler then gets wrapped by our new ErrorLogHandlerFactory (the one overriding the original one from ELMAH) into another one requiring session state (SessionRequiringHandler). When ASP.NET sees the handler and that it implements IRequiresSessionState, it goes ahead and makes the session state available to the request context. Later when the handler is just about to be executed, ASP.NET raises the PreRequestHandlerExecute event, to which ElmahAuthorizationHandler subscribes. It is then during this event that the real authorization check is conducted, the one based on session state. I've chosen to implement IRequiresSessionState on RequiringSessionState to have a read-write access to session state for demo purposes but you can decide to use IReadOnlySessionState if it is good enough for your case.
 
Hope this helps.
 
- Atif
 
 
authdemo.zip
Reply all
Reply to author
Forward
0 new messages