Mako Authentication Required

I have been using my mod_python Mako handler for several months now in my personal projects. For the most part, I have been very happy with Mako and am finding it extremely useful. One issue I have had to wrap my head around has been the inability to halt template execution cleanly. A common practice in many a website has been to flush the current output buffer, display the required authentication information with a form or a redirect, and then end the request—making authentication required on a page of content.

Mako posed a significant challenge to my own thought process as I was designing page templates. I was very used to having the content buffered and being able to perform an internal redirect or transfer. Both mod_perl and ASP.NET offer this feature as part of the various frameworks. Luckily, my work in PHP has helped me pay much closer attention to how I am handling the internal flow of programs. Since most PHP configurations are set to be unbuffered, you have to pay particular attention to when and where you are delivering headers to the client browser.

One way to do solve this was to introduce exception handling to my mako handler. In ASP.NET, when an internal transfer or redirect is made an exception is thrown through the stack. In almost all cases, this exception can be cleanly ignored. To introduce this behavior, I would need to add acceptable exception conditions to my handler. Further, I would have to code my specific authentication methods into my generic handler. This was something I did not wish to do.

Instead of working with exceptions, I looked to the template inheritance of Mako to solve the problem. I start with a non-authenticated template which is in turn inherited by an authenticated template. The page requested inherits the template that corresponds to its need for authentication.

The following example pages illustrate how inheritance can be used to introduce the required authentication.

template.html

<%
    self.page_executed = False
%>
<html><body>
${next.body()}
<br /><br />
Page Executed: ${self.page_executed}
</body></html>

require_auth_template.html

<%inherit file="template.html" />
<%!
    from my_library import Account
%>
<%
    self.account = Account.fetch_from_request(req)
%>
% if self.account is None:
    You must log in to continue.
% else:
    ${next.body()}
% endif

page1.html

<%inherit file="template.html" />
<%
    self.page_executed = True
%>
This page will always be displayed in the template.

page2.html

<%inherit file="require_auth_template.html" />
<%
    self.page_executed = True
%>
This page will only be displayed in the template
if the user has logged in.

Output of page1.html

This page will always be displayed in the template.

Page Executed: True

Output of page2.html

You must log in to continue.

Page Executed: False

Because the user is not logged in, the inheritance chain of page2.html ends the request and sends the login content to the top-level template without even calling the template of page2.html.

This technique can be further expanded to introduce exception handling by wrapping the next.body() call in a try block. This would keep all of the necessary exception and authentication logic outside of template handler and in the templates where they belong.

Attachments:
January 21st, 2010