<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Gustavo on Information Technology &#187; Software development</title>
	<atom:link href="http://gustavonarea.net/blog/topics/software-development/feed/" rel="self" type="application/rss+xml" />
	<link>http://gustavonarea.net</link>
	<description>Just a social techie</description>
	<lastBuildDate>Tue, 20 Jul 2010 20:47:38 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Web Site Security With repoze.who and repoze.what</title>
		<link>http://gustavonarea.net/blog/posts/repoze-auth/</link>
		<comments>http://gustavonarea.net/blog/posts/repoze-auth/#comments</comments>
		<pubDate>Tue, 01 Jun 2010 14:41:13 +0000</pubDate>
		<dc:creator>Gustavo</dc:creator>
				<category><![CDATA[HOWTOs]]></category>
		<category><![CDATA[Publications]]></category>
		<category><![CDATA[Software development]]></category>
		<category><![CDATA[Turbo Gears]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Python Magazine]]></category>
		<category><![CDATA[repoze.what]]></category>
		<category><![CDATA[repoze.who]]></category>
		<category><![CDATA[TurboGears]]></category>
		<category><![CDATA[WSGI]]></category>

		<guid isPermaLink="false">http://gustavonarea.net/?p=294</guid>
		<description><![CDATA[This article first appeared in the May 2009 issue of Python Magazine and has been slightly updated. The contents of the article are only applicable to repoze.who 1.0 and repoze.what 1.0, not repoze.who 2 and repoze.what 1.1 which are under development as of this writing.
Have you ever created a Web application? If so, it&#8217;s very [...]]]></description>
			<content:encoded><![CDATA[<p><em>This article first appeared in the <a href="http://pymag.phparch.com/c/issue/view/98">May 2009 issue of Python Magazine</a> and has been slightly updated. The contents of the article are only applicable to repoze.who 1.0 and repoze.what 1.0, <strong>not</strong> repoze.who 2 and repoze.what 1.1 which are under development as of this writing.</em></p>
<p>Have you ever created a Web application? If so, it&#8217;s very likely that you have at one time or another faced &#8220;the security problem&#8221;; whether to create and maintain a homegrown security sub-system, or to learn to use framework-specific security mechanisms (which may not be as flexible as you wish).</p>
<p><strong>Securing Web applications shouldn&#8217;t be a problem</strong>. This article explores a highly extensible alternative which you can learn once and use in arbitrary applications, regardless of the Web framework used (if any!).<br />
<span id="more-294"></span><br />
Application security is a broad field within software development, covering topics ranging from low-level network transmission security and encryption, up to application-level data security and input validation. In this article, we will focus on two of the most basic elements of application security: authentication and authorization.</p>
<h2>Authentication vs. Authorization</h2>
<p>Even experienced software developers often confuse these two related but not equivalent terms, so we&#8217;ll start by explaining what they are.</p>
<p><em>Authentication</em>, often shortened as &#8220;authn&#8221;, is what you do when you check the credentials provided by the user to verify that he&#8217;s really who he claims to be. The most widely-used credentials are a set made up of a user identifier (like user name, or email address) and a password.</p>
<p><em>Authorization</em>, often shortened as &#8220;authz&#8221;, is what you do when you check whether the user has permission to make the request or perform the transaction he&#8217;s currently making. Most of the time this depends on <strong>who</strong> the subject is, but it may also depend on <strong>what</strong> is requested, <strong>when</strong> it is requested, and/or <strong>how</strong> it is requested.</p>
<p>A common shortening for &#8220;authentication and authorization&#8221; is &#8220;auth&#8221;.</p>
<p>And there&#8217;s one more related term that comes into play: <em>Identification</em>, which is to check whether he was successfully authenticated previously to avoid challenging him again unnecessarily.</p>
<h2>Authentication, identification and authorization in Python Web applications</h2>
<p>Web applications powered by Python may take advantage of <a href="http://docs.repoze.org/who/1.0/">repoze.who</a> to handle authentication and identification, and <a href="http://what.repoze.org/docs/1.0/">repoze.what</a> to handle authorization. Both are security frameworks that can be used on top of your application, or integrated with your application&#8217;s Web framework if you are using one.</p>
<p>You can use them as long as your application is WSGI-compliant. WSGI is a Python standard which defines an interface between HTTP servers and Python applications, similar to <a href="http://en.wikipedia.org/wiki/Common_Gateway_Interface">CGI</a>, which eases the writing of cross Web server libraries (which can be framework-independent) and applications.</p>
<p>The WSGI standard mandates the availability of a Python dictionary,  referred to as the &#8220;WSGI environment&#8221;.  This dictionary stores CGI environment variables, as well as others specific to the WSGI standard; other components used by your application may also use the WSGI environment to store data.</p>
<p>Any framework or raw application that exposes the WSGI environment dictionary can use both repoze.who and repoze.what.  The popular WSGI-compliant frameworks <a href="http://www.cherrypy.org/">CherryPy</a>, <a href="http://www.djangoproject.com/">Django</a>, <a href="http://pylonshq.com/">Pylons</a>, <a href="http://turbogears.org/">TurboGears</a> and <a href="http://werkzeug.pocoo.org/">Werkzeug</a> expose this variable.</p>
<h2>How repoze.who and repoze.what work</h2>
<p>Figure 1 illustrates how a typical request is processed in WSGI and when repoze.who comes into play.</p>
<div id="attachment_310" class="wp-caption alignnone" style="width: 810px"><img class="size-full wp-image-310" title="Figure 1" src="/uploads/Figure1.png" alt="Figure 1" width="800" height="566" /><p class="wp-caption-text">Figure 1</p></div>
<p>repoze.who is a WSGI middleware. A WSGI middleware is a layer that wraps your application, processing each request before your application does. WSGI middleware can also post-process your application&#8217;s response. WSGI applications can have zero or more of these middleware layers.</p>
<p>As shown in Figure 2, when a request is made and before it reaches your application, repoze.who will try to check if the user is already logged in or if he&#8217;s currently trying to log in. If the user is trying to log in and has supplied the right credentials, authentication will succeed and the user will be &#8220;remembered&#8221; in future requests by default. The request is then passed to the next WSGI middleware, if any, and eventually on to your WSGI application.</p>
<div id="attachment_313" class="wp-caption alignnone" style="width: 576px"><img class="size-full wp-image-313" title="Figure 2" src="http://gustavonarea.net/uploads/Figure2.png" alt="Figure 2" width="566" height="800" /><p class="wp-caption-text">Figure 2</p></div>
<p>The following fully customizable components can be used at this stage:</p>
<ul>
<li>The request classifier: It matters if the agent is a browser, a Subversion client or a library which access a Web Service; you wouldn&#8217;t want to display a login form if the agent is a Subversion client, for example. Thus, this component classifies the current request so that only the appropriate plugins (out of your pre-selected ones) are used.</li>
<li>Identifier plugins: These are the components that &#8220;identify&#8221; the user. That is, they are able to tell if the user is already logged from in a previous request, or if he is trying to log in in the current request. When authentication succeeds, these plugins are in charge of &#8220;remembering&#8221; the user for future requests (e.g., by defining a session cookie). Likewise, when a challenge is required, they &#8220;forget&#8221; the user (discarding a session cookie).</li>
<li>Authenticator plugins: When the user is trying to log in in the current request, these components verify the user-supplied credentials (such as username and password) against a database, LDAP server, .htaccess file, etc.</li>
<li>Metadata provider plugins: If the user was authenticated, metadata providers can load data about the current user (email address, full name, etc.) so that such data is ready to be used by your application.</li>
</ul>
<p>After your application issues a response and before it reaches the HTTP server, as shown in Figure 3, repoze.who will check if a challenge is necessary; that is, ask the user in some way to identify himself.  If so and the user was previously authenticated, the previous authentication will be forgotten, and the related session cookie will be discarded. After the challenge is complete, your application&#8217;s response will be replaced with a new response which will allow the user to log in (for example, sending a &#8220;WWW-Authenticate&#8221; header or displaying a login form).</p>
<p><img class="alignnone size-full wp-image-365" title="Figure 3" src="http://gustavonarea.net/uploads/Figure3.png" alt="repoze.who on egress" width="566" height="800" /></p>
<p>repoze.who&#8217;s response-handling functionality is also driven by customizable components:</p>
<ul>
<li>The challenge decider: This is the component which will determine whether a challenge is required. The default challenge decider will order a challenge if and only if the response&#8217;s HTTP status code is 401.</li>
<li>Challenger plugins: When a challenge is required, those challenger plugins which support the current request type will be run until the first of them returns a valid response.</li>
</ul>
<p>repoze.who may seem hard to use at first sight, with all its components and terminology; so much flexibility comes at the cost of being a little hard to understand. But one of the goals of this article is to help people who had never heard of repoze.who to deal with basic and advanced authentication settings.</p>
<h2>And how does repoze.what work?</h2>
<p>repoze.what is mostly used inside of your application, where you define the access rules that must be met for a given routine to be performed.</p>
<p>Access rules in repoze.what are made of atomic units called &#8220;predicate checkers&#8221;, re-usable objects that check whether a given condition is met.  Predicate checkers can be single or compound to form complex access rules. For example, a single condition (or predicate) might be &#8220;The user is not anonymous&#8221;, while a compound predicate could be &#8220;The user is not anonymous <em>and</em> their IP address is X.X.X.X&#8221;. You can write your own predicate checkers, usually in just a few lines of code.</p>
<p>repoze.what has built-in support for the widely-used authorization pattern whereby you assign groups to your users and then grant permissions to such groups; it ships with a comprehensive set of checkers for the relevant predicates (e.g., &#8220;The current user belongs to the &#8216;directors&#8217; group&#8221;, &#8220;The current user is allowed to edit user accounts&#8221;).  If you use this authorization pattern, the groups and permissions for the authenticated user will be loaded by a repoze.who metadata provider; in repoze.what 1.1, they&#8217;ll be loaded on demand by repoze.what itself, so you wouldn&#8217;t need repoze.who.  Nevertheless, it is entirely optional and you can use any authorization pattern that best suits your needs.</p>
<p>Predicate checkers allow you to control access based not only on <em>who</em> makes the request, but also on <em>how</em> the request is made and <em>what</em> exactly is requested. For example, if you have a blog application, you might use the simple predicate &#8220;The user is allowed to remove posts&#8221;, focusing on who the user is to control access to the post edition routine. Or you can have the more specific compound predicate &#8220;The user is allowed to remove posts, as long as the user is the post&#8217;s author&#8221;.  This predicate focuses on both <em>who</em> makes the request and <em>what</em> is requested. You can have an even more complex access rule for the article edition routine, such as &#8220;The user is allowed to remove posts, as long as the user is the post&#8217;s author &#8212; except post #1 which nobody can remove&#8221;.</p>
<p>repoze.what 1.0.X supports only blacklist-based authorization, that is, authorization is granted unless explicitly denied. Whitelist-based authorization, in which authorization is denied unless explicitly granted, will be supported as of version 1.1 (under development as of this writing).</p>
<h2>Creating a Web application protected with repoze.who and repoze.what</h2>
<p>It&#8217;s time to go practical!  We&#8217;re going to create a WSGI application powered by repoze.who and repoze.what.</p>
<p>I&#8217;ll use the TurboGears 2 Web Application framework to make this simple application, but after reading the article you should be able to put what you just learned into practice in other frameworks, or standalone applications.</p>
<p>(For the sake of demonstration, we will skip typical overhead features such as input validation, so we can focus the examples on the repoze.who and repose.what&#8217;s integration of authentication and authorization.)</p>
<p>Let&#8217;s get started!</p>
<p>We&#8217;re going to develop this application using an isolated Python environment using a rather famous utility called &#8220;virtualenv&#8221;. This is very handy because everything you install or remove won&#8217;t affect your system-wide Python environment. To install it, run:</p>
<p><code>easy_install virtualenv</code></p>
<p>You may need administration rights to install it, depending on where you install it.</p>
<p>Next, create an environment for our application and activate it; on Unix systems, the commands for this are:</p>
<p><code>virtualenv --no-site-packages appenv<br />
source appenv/bin/activate</code></p>
<p>On Windows systems, enter the following in a directory whose path doesn&#8217;t contain spaces:</p>
<p><code>C:\Python25\python.exe "C:\Path-to-VE\virtualenv.py" appenv<br />
C:\Path-to-newly-created-environment\Scripts\activate.bat</code></p>
<p>When you&#8217;re done and want to deactivate it, you should run the command &#8220;deactivate&#8221; on Unix systems. For Windows, use &#8220;C:\Path-to-newly-created-environment\Scripts\deactivate.bat&#8221;.</p>
<p>I&#8217;ve called my virtual environment &#8220;appenv&#8221;, but you can use any name you like.</p>
<p>For help installing virtualenv, you can check its documentation at  <a href="http://pypi.python.org/pypi/virtualenv">http://pypi.python.org/pypi/virtualenv</a>.</p>
<p>Now to install TurboGears 2:</p>
<p><code>easy_install -i http://www.turbogears.org/2.0/downloads/current/index tg.devtools</code></p>
<h2>Generating an application</h2>
<p>TurboGears allows you to start coding using a minimal application, so that you don&#8217;t have to start from scratch (unless you really want to).  We&#8217;ll use just that minimal application so that we can get off to a quick start, which you can optionally <a href="/files/articles/pymag-repoze-09/classifieds.zip" title="Download the sample classifieds application">download</a>.</p>
<p>The Web application we&#8217;re going to create is a classifieds service, like craiglist.org or gumtree.com; for lack of imagination, I&#8217;ll call it &#8220;classifieds&#8221;. To start coding it from a minimal application, we&#8217;ll ask TurboGears to generate it with the following command:</p>
<p><code>paster quickstart classifieds</code></p>
<p>Then you&#8217;ll be asked a couple of questions.  The first will ask you for the package name to be generated in this project; hit ENTER to accept &#8220;classifieds&#8221; as the default package name.  The second question will ask if you need authentication and authorization features for this project, hit ENTER to accept the default answer of &#8220;yes&#8221;.  By answering &#8220;yes&#8221; to the second question, repoze.who and repoze.what will be used in the application by default. Now switch to the application&#8217;s directory, install it in development mode and set it up:</p>
<p><code>cd classifieds<br />
python setup.py develop<br />
paster setup-app development.ini<br />
</code></p>
<p>We&#8217;re not going to start coding yet &#8212; I&#8217;d like you to try the generated application, so that you can see repoze.who and repoze.what in action. So, start the application (the &#8220;reload&#8221; switch will restart the application whenever one of its files is modified):</p>
<p><code>paster serve --reload development.ini</code></p>
<p>and then open the following URL in your browser: <a href="http://localhost:8080/">http://localhost:8080/</a>.</p>
<p>You should keep this simple procedure in mind because we&#8217;ll use it very often. Then, when you need to stop the development server, you have to hit Ctrl+C.</p>
<p>Now try, at least, the following (in order):</p>
<ol>
<li><strong>Log in</strong>: Visit <a href="http://localhost:8080/login">http://localhost:8080/login</a> or click on the &#8220;Login&#8221; link in the upper-right side of any Web page of our application. Enter &#8220;manager&#8221; and &#8220;managepass&#8221; in the login and password fields. You&#8217;ll get redirected to the main page and a welcome message will be displayed.</li>
<li><strong>Visit a page you shouldn&#8217;t see</strong>: Like <a href="http://localhost:8080/editor_user_only">http://localhost:8080/editor_user_only</a>; you should get a 403 page and a message which reads &#8220;Only for the editor&#8221;.</li>
<li><strong>Log out</strong>: Visit <a href="http://localhost:8080/logout_handler">http://localhost:8080/logout_handler</a> or click on the &#8220;Logout&#8221; link in the upper-right side of any Web page of our application. You&#8217;ll get redirected to the main page and a goodbye message will be displayed.</li>
<li><strong>Visit a private page as anonymous</strong>: If you&#8217;re currently logged in, log out first. Then visit <a href="http://localhost:8080/manage_permission_only">http://localhost:8080/manage_permission_only</a>; you should be redirected to the login form, where also the message &#8220;Only for managers&#8221; is displayed. Log in with the previous credentials (&#8220;manager&#8221;/&#8221;managepass&#8221;) and you&#8217;ll be redirected to the page you requested initially.</li>
</ol>
<p>What you&#8217;ve seen is repoze.who, repoze.what and some of their official plugins in action; those notification messages are TurboGears-specific, though, but it shouldn&#8217;t be hard to port them to other frameworks or raw applications.</p>
<p>Before moving forward, I&#8217;ll describe some of the files and directories that the &#8220;paster quickstart&#8221; command above generated:</p>
<ul>
<li>devdata.db: The sqlite database file used for development.</li>
<li>classifieds/: Your application&#8217;s package itself.</li>
<li>classifieds/config/middleware.py: The file where the extra WSGI middleware for your application is added.</li>
<li>classifieds/controllers/root.py: Your application&#8217;s root controller. Sub-controllers should be attached to the one defined in this file; below I&#8217;ll explain how.</li>
<li>classifieds/model/: The application&#8217;s model definitions powered by <a href="http://www.sqlalchemy.org/">SQLAlchemy</a>.</li>
<li>classifieds/model/auth.py: The model definitions specific to authentication, identification and authorization.</li>
<li>classifieds/templates/: The application&#8217;s XHTML templates powered by <a href="http://genshi.edgewall.org/">Genshi</a>.</li>
<li>classifieds/websetup.py: Contains the function which will get run when the application is set up (used by the <em>paster setup-app</em> command). By default it just adds rows to the database.</li>
</ul>
<p>On the other hand, this is how authentication, identification and authorization is configured right now:</p>
<ul>
<li>We have a table for our application&#8217;s users (<em>tg_user</em>), which contains the self-explanatory fields <em>user_name</em> and <em>password</em>, among others. repoze.who is configured to authenticate by using these two fields.</li>
<li>repoze.what is configured to use its groups/permission-based pattern. The groups and permissions are stored in the database, in the <em>tg_group</em> and <em>tg_permission</em> tables.</li>
<li>One user can belong to zero or more groups; one group can be granted zero or more permissions.</li>
<li>Right now we have two users, &#8220;manager&#8221; and &#8220;editor&#8221; (with passwords &#8220;managepass&#8221; and &#8220;editpass&#8221;). &#8220;manager&#8221; belongs to the only group defined so far, &#8220;managers&#8221;. The  group &#8220;managers&#8221; is granted the only permission defined so far, &#8220;manage&#8221;.</li>
</ul>
<h2>Let&#8217;s start coding</h2>
<p>&#8220;At last!&#8221;, I heard you say.</p>
<p>While implementing repoze.who and repoze.what in an application which doesn&#8217;t come with them enabled out-of-the-box, the first thing you have to do is add their middleware to your application.</p>
<p>However, we&#8217;ll skip that part and keep the default settings for now, so that we can go to fun part right away: Learning how to protect areas in your Web application. The setup will be addressed later on, where you&#8217;ll learn how to configure repoze.who and repoze.what the TurboGears-independent way.</p>
<p>This is what we&#8217;re going to implement in our classifieds application:</p>
<ul>
<li>A simple user registration system.</li>
<li>A classified visualization mechanism, for any user (anonymous or authenticated).</li>
<li>A classified addition mechanism, for registered users.</li>
<li>A classified edition mechanism, for registered users to edit their own classifieds.</li>
</ul>
<p>So it&#8217;s finally time to fire up your Python editor, a terminal and a window of your browser!</p>
<p>First of all, let&#8217;s add groups and permissions for authorization in our application, instead of sticking to the ones created by default. We&#8217;re going to add one more group called &#8220;posters&#8221; and the &#8220;add-classifieds&#8221; permission to be granted to posters. (Before continuing, you should stop the server running in the terminal &#8212; With Ctrl+C).</p>
<p>To add the posters group, add this code to the <em>setup_app()</em> function, defined in <em>classifieds/websetup.py</em>:</p>
<p><code>posters = model.Group(group_name=u'posters', display_name=u'Classified posters')<br />
model.DBSession.add(posters)</code></p>
<p>The addclassifieds permission is created with this code:</p>
<p><code>addclassifieds = model.Permission(permission_name=u'add-classifieds', description=u'Allowed to add classifieds')<br />
addclassifieds.groups.append(posters)<br />
model.DBSession.add(addclassifieds)</code></p>
<p>Add these sections <strong>right before</strong> the following lines:</p>
<p><code>model.DBSession.flush()<br />
transaction.commit()</code></p>
<p>Now, since we have a classifieds application, we have to define the SQLAlchemy model for the classifieds. To keep things simple, we&#8217;ll define just four columns:</p>
<ul>
<li><em>classified_id</em>: The classified&#8217;s identifier.</li>
<li><em>poster_id</em>: The poster&#8217;s user id.</li>
<li><em>classified_title</em>: The classified title.</li>
<li><em>classified_contents</em>: The classified contents.</li>
</ul>
<p><a href="/files/articles/pymag-repoze-09/Listing1.py.txt">Listing 1</a> implements this model definition. You have to create <em>classifieds/model/posts.py</em> and store that definition in there. Then you have to import that model at the end of <em>classifieds/model/__init__.py</em>:</p>
<p><code>from classifieds.model.posts import Classified</code></p>
<p>To apply the changes, let&#8217;s remove the development database, re-create it with our new model and rows, and finally start the server again:</p>
<p><code>rm devdata.db<br />
paster setup-app development.ini<br />
paster serve --reload development.ini<br />
</code></p>
<p>That&#8217;s it, now we have the model definition for the classifieds. Let the fun part begin!</p>
<h2>Creating a user registration system</h2>
<p>We&#8217;re going to create a simple registration system made up of two controller actions: One to display the registration form and another to process the submitted form.</p>
<p>We&#8217;re going to implement them in the root controller for our application, the class <em>RootController</em> found in the source file <em>classifieds/controllers/root.py</em>.</p>
<p>To define the action for the registration form, first import the repoze.what predicate checker that verifies that the current user is anonymous:</p>
<p><code>from repoze.what.predicates import is_anonymous</code></p>
<p>Next, add the following method in <em>RootController</em>:</p>
<p><code>@expose('classifieds.templates.register')<br />
@require(is_anonymous(msg='Only one account per user is allowed'))<br />
def register(self):<br />
    """Display the user registration form"""<br />
    return {'page': 'user registration'}</code></p>
<p>What the two decorators above do is specify that the template for the &#8220;register&#8221; action is <em>classifieds/templates/register.html</em> and that access is granted to users who don&#8217;t have an account, respectively.</p>
<p><em>@require</em> is a TurboGears-specific decorator that evaluates a repoze.what predicate before calling the action in question. When such a predicate is not met, the action is not called and a 401 response is returned (403 if the user is already logged in). Then repoze.who&#8217;s default challenge decider will find the 401 response and will replace it with the challenge.</p>
<p>Internally, the predicate is evaluated by calling its <em>check_authorization()</em> method (which raises the <em>repoze.what.predicates.NotAuthorizedError</em> exception if the predicate isn&#8217;t met, whose message is the user-friendly explanation) or <em>is_met()</em> (which returns a boolean).  <a href="/files/articles/pymag-repoze-09/Listing2.py.txt">Listing 2</a> illustrates a fictitious implementation, using the <em>decorator</em> package; you&#8217;d find it useful if you want to use repoze.what in another framework or raw application (Pylons users may want to check <a title="The Pylons plugin for repoze.what" href="http://code.gustavonarea.net/repoze.what-pylons/">repoze.what-pylons</a>).</p>
<p>Going back to the creation of the action, you&#8217;ll have to create the template that will display the form: Create <em>classifieds/templates/register.html</em> with the contents of <a href="/files/articles/pymag-repoze-09/Listing3.html.txt">Listing 3</a>.</p>
<p>Now it&#8217;s time to create the action which will process the contents of the submitted form by adding the user to the database, including them in the &#8220;posters&#8221; group and finally redirecting them to the login form so that they can use the newly created account. For that, you have to define the <em>add_user</em> method shown in <a href="/files/articles/pymag-repoze-09/Listing4.py.txt">Listing 4</a> (in <em>RootController</em>).</p>
<p>That&#8217;s it! Our registration system is done. Now you can try it by visiting <a href="http://localhost:8080/register">http://localhost:8080/register</a>.</p>
<h2>The classifieds visualization mechanism</h2>
<p>We&#8217;re going to write the part of the interface which will allow us to see the classifieds hosted by our service. This will be accomplished by means of two controller actions: One to see all the classifieds available, in the main page of the application, and another to see individual classifieds.</p>
<p>For the first, we have to re-write the &#8220;index&#8221; action of the root controller, to make it look like this:</p>
<p><code>@expose('classifieds.templates.index')<br />
def index(self):<br />
    query = DBSession.query(model.Classified)<br />
    all_classifieds = query.all()<br />
    return dict(page='index', classifieds=all_classifieds)</code></p>
<p>Then its template, <em>classifieds/templates/index.html</em>, should be replaced with the contents of <a href="/files/articles/pymag-repoze-09/Listing5.html.txt">Listing 5</a>, which lists the available classifieds with a link to their individual pages.</p>
<p>The second action, the one to show the classifieds individually, will be implemented as &#8220;view&#8221; and will be defined in the root controller too:</p>
<p><code>@expose('classifieds.templates.view')<br />
def view(self, classified):<br />
    query = DBSession.query(model.Classified)<br />
    classified_obj = query.get(classified)<br />
    # Is the user allowed to edit the classified?<br />
    checker = user_is_poster()<br />
    can_edit = checker.is_met(request.environ)<br />
    return {'page': 'Classified page', 'classified': classified_obj, 'classified_id': classified, 'can_edit': can_edit}</code></p>
<p>Note that in the &#8220;view&#8221; action we do something new: Handle a predicate checker directly. Sometimes it is necessary to evaluate them directly and get a boolean result to know whether it&#8217;s met or not thanks to the <em>is_met()</em> method of the predicate checker. For example, right now we use it to display a link to edit that predicate, if and only if the current user is allowed to edit it.</p>
<p>Then the template for &#8220;view&#8221;, <em>classifieds/templates/index.html</em>, is defined as shown in <a href="/files/articles/pymag-repoze-09/Listing6.html.txt">Listing 6</a>.</p>
<h2>Implementing the classified submission mechanism</h2>
<p>To allow users publish classifieds, we&#8217;ll write two controller actions (once again, one to display the form and the other to process it).</p>
<p>The action that will display the form should first check that the user is allowed to add a classified; this is, we should use repoze.what&#8217;s <em>has_permission</em> predicate so that it checks whether they are granted the &#8220;add-classified&#8221; permission. You have to import it at the top of <em>classifieds/controllers/root.py</em>:</p>
<p><code>from repoze.what.predicates import has_permission</code></p>
<p>And then write the action as shown below:</p>
<p><code>@expose('classifieds.templates.add')<br />
@require(has_permission('add-classifieds'))<br />
def add(self):<br />
    return {'page': 'Classified submission'}<br />
</code></p>
<p>This action uses the template <em>classifieds/templates/add.html</em>, which we have to create with the contents of <a href="/files/articles/pymag-repoze-09/Listing7.html.txt">Listing 7</a>.</p>
<p>Finally, <a href="/files/articles/pymag-repoze-09/Listing8.py.txt">Listing 8</a> shows how the action that processes the submitted form is implemented. There we use something we hadn&#8217;t used before: The repoze.who identity dictionary. Do you remember that I said that repoze.who optionally uses so-called &#8220;metadata providers&#8221;, which are plugins that load data about the current user into the request? Well, that data is kept in the WSGI environment dictionary, under the <em>repoze.who.identity</em> key.</p>
<p>Then we use one of the items of that dictionary, &#8220;user&#8221;, which contains the database object for the current user. It is loaded by the metadata provider defined in the repoze.who SQLAlchemy plugin (repoze.who.plugins.sa), a component that is enabled by default in TurboGears.</p>
<h2>Implementing the classified edition mechanism with custom predicate checkers</h2>
<p>So far we&#8217;ve used a few repoze.what predicate checkers, which are all very basic. Very often you have to write your own checkers. For example, if the URL where classifieds are edited looks like <q>http://localhost:8080/edit/<em>X</em></q> (where &#8220;X&#8221; represents the classified identifier), and we want that classifieds can only be edited by their posters, we&#8217;ll need a predicate checker that finds the poster of the &#8220;X&#8221; classified and checks if it is the current user.</p>
<p>This predicate checker is implemented in <a href="/files/articles/pymag-repoze-09/Listing9.py.txt">Listing 9</a>, which should be stored in <em>classifieds/lib/authz.py</em> (a file you should create). That&#8217;s a good sample checker, which helps us to understand how a predicate checker is defined:</p>
<ul>
<li>It must extend the <em>repoze.what.predicates.Predicate</em> class.</li>
<li>It must define a user-friendly explanation in the <em>message</em> attribute, which may be shown to the user when the predicate is not met.</li>
<li>Its logic is defined in the <em>evaluate()</em> instance method, which must call the <em>unmet()</em> method when the predicate is not met; keyword arguments passed to this method will replace the placeholders defined in <em>message</em> (if any), although that message can be replaced on-the-fly with a string passed as the first positional argument. <em>evaluate()</em> receives the WSGI environment and the repoze.what credentials dictionaries as arguments.</li>
<li>If the checker relies on arguments such as GET or POST variables, or other arguments available in the URL, the <em>parse_variables()</em> method should be used to retrieve them. It will return a dictionary whose items are: &#8220;get&#8221; for variables in the query string and &#8220;post&#8221; for POST variables, plus &#8220;named_args&#8221; and &#8220;positional_args&#8221; for named and positional arguments in the URL (which must be set by a routing software like Selector or Routes).</li>
</ul>
<p>Because this predicate relies on a named argument in the URL (&#8220;classified&#8221;), we have to use a URL router software compliant with the <a href="http://wsgi.org/wsgi/Specifications/routing_args">wsgiorg.routing_args</a> standard; we&#8217;ll use Routes. To configure Routes in TurboGears 2, you have to insert the following contents in <em>classifieds/config/app_cfg.py</em> (right after the imports) and then replace the line <em>base_config = AppConfig()</em> with <em>base_config = ClassifiedsConfig()</em>:</p>
<p><code>class ClassifiedsConfig(AppConfig):<br />
def setup_routes(self):<br />
    """Customize routing"""<br />
    from tg import config<br />
    from routes import Mapper<br />
    dir = config['pylons.paths']['controllers']<br />
    map = Mapper(directory=dir, always_scan=config['debug'])<br />
    # Defining our custom routes:<br />
    map.connect('/{action}/{classified:\d+}', controller='root')<br />
    # Required by TurboGears:<br />
    map.connect('*url', controller='root', action='routes_placeholder')<br />
    config['routes.map'] = map<br />
</code></p>
<p>At this point we&#8217;re ready to use the <em>user_is_poster</em> checker.</p>
<p>Now import the custom checker into <em>classifieds/controllers/root.py</em>:</p>
<p><code>from classifieds.lib.authz import user_is_poster</code></p>
<p>Next, use the following contents to define the &#8220;edit&#8221; action and the contents of <a href="/files/articles/pymag-repoze-09/Listing10.html.txt">Listing 10</a> for its template (<em>classifieds/templates/edit.html</em>):</p>
<p><code>@expose('classifieds.templates.edit')<br />
@require(user_is_poster())<br />
def edit(self, classified):<br />
    query = DBSession.query(model.Classified)<br />
    classified_obj = query.get(classified)<br />
    return {'page': 'Classified edition page', 'classified': classified_obj, 'classified_id': classified}</code></p>
<p>Finally, for the action that processes the submitted form, we can use:</p>
<p><code>@expose()<br />
@require(user_is_poster())<br />
def edit_classified(self, classified, title, contents):<br />
    # Fetching and updating the classified object:<br />
    query = DBSession.query(model.Classified)<br />
    classified_obj = query.get(classified)<br />
    classified_obj.classified_title = title<br />
    classified_obj.classified_contents = contents<br />
    DBSession.update(classified_obj)<br />
    # Notifying the user:<br />
    flash('Classified "%s" updated!' % title)<br />
    redirect(url('/view/%s' % classified))<br />
</code></p>
<p>You can now play with the classifieds edition mechanism, to see by yourself how authorization is denied when somebody tries to edit somebody else&#8217;s classified, thanks to our &#8220;user_is_poster&#8221; checker.</p>
<h2>Configuring it all by ourselves</h2>
<p>At this point you&#8217;re able to deal with identification (using the repoze.who identity dictionary, which contains user data) and control authorization in your application with repoze.what predicate checkers (even how to write your own!), and you should also be able to put this knowledge to the test in frameworks other than TurboGears.</p>
<p>What we&#8217;re missing now is to know how to configure repoze.who and repoze.what by ourselves, since we skipped that part initially because TurboGears configures them for us. But you have to know this to use both packages with other frameworks or even to continue with TurboGears in a more advanced setup.</p>
<p>Therefore we&#8217;re going to stop TurboGears from configuring repoze.who and repoze.what, so that we have full control on how they are configured and learn how to do it in other frameworks.</p>
<p>To disable the automatic setup of repoze.who and repoze.what, go to <em>classifieds/config/app_cfg.py</em> and set the variable <em>base_config.auth_backend</em> to <em>None</em>. Then all those variables that start by <em>base_config.sa_auth</em> will be ignored, so you can remove them if you want.</p>
<p>Now let&#8217;s handle the configuration by adding the middleware to our WSGI application. I&#8217;ll use a function called <em>add_auth()</em> (defined in <em>classifieds/config/auth.py</em>) which receives the WSGI application as the only argument and returns it with the middleware added, as shown in <a href="/files/articles/pymag-repoze-09/Listing11.py.txt">Listing 11</a>.</p>
<p>Because <em>add_auth()</em> configures repoze.who and repoze.what the same way we&#8217;ve been using them, but with the hidden details revealed, we&#8217;ll be able to identify their components.</p>
<p>In this function we see that repoze.who is configured with the following plugins:</p>
<ul>
<li><em>AuthTktCookiePlugin</em>, an identifier which remembers and forgets authenticated users using cookies (using the string &#8220;secret&#8221; as the encryption key and &#8220;authtkt&#8221; as the cookie name).</li>
<li><em>FriendlyFormPlugin</em>, the component that handles our login form and logouts. As an identifier, when the user is logging in it extracts the &#8220;login&#8221; and &#8220;password&#8221; from the request so that the authenticator(s) can use such data, and when the user tries to log out, it asks <em>AuthTktCookiePlugin</em> to forget the user. As a challenger, it redirects the user to the login form (at &#8220;/login&#8221;).</li>
<li><em>SQLAlchemyAuthenticatorPlugin</em>, as the only authenticator used. It connects to the &#8220;tg_user&#8221; table to check if there&#8217;s a match for the supplied &#8220;login&#8221; and &#8220;password&#8221;.</li>
<li><em>SQLAlchemyUserMDPlugin</em>, the metadata provider that loads the current user&#8217;s SQLAlchemy object into the repoze.who identity item &#8220;user&#8221;.</li>
<li>Because we didn&#8217;t specified request classifiers or challenge deciders, the default ones will be used.</li>
</ul>
<p>Meanwhile, repoze.what is configured using the groups/permissions-based authorization pattern, where the groups and permissions are retrieved thanks to the following adapters:</p>
<ul>
<li><em>SqlGroupsAdapter</em>, which loads the groups from the &#8220;tg_group&#8221; table.</li>
<li><em>SqlPermissionsAdapter</em>, which loads the groups from the &#8220;tg_permission&#8221; table.</li>
</ul>
<p>If we don&#8217;t use this authorization pattern, our <em>app_with_mw</em> variable would have been defined without passing groups/permission adapters:</p>
<p><code>app_with_mw = setup_auth(app, **who_args)</code></p>
<p>And what about using repoze.who but not repoze.what? <a href="/files/articles/pymag-repoze-09/Listing12.py.txt">Listing 12</a> shows how to configure repoze.who just like we did above, but without repoze.what. Note that this time we had to pass the request classifier and challenge decider explicitly.</p>
<p>The opposite, using repoze.what without repoze.who, is not yet possible as of this writing because repoze.what 1.0&#8217;s credentials are loaded through a repoze.who metadata provider. repoze.what 1.1 will be completely repoze.who-independent, optionally.</p>
<p>Finally, it&#8217;s time to use <em>add_auth()</em>. It can be used like any other WSGI middleware, so in the case of this TurboGears 2 application, it is in <em>classifieds/config/middleware.py</em>; if using another framework, consult the relevant documentation to find the equivalent. First, you should import the function:</p>
<p><code>from classifieds.config.auth import add_auth</code></p>
<p>And then use it inside the <em>make_app()</em> function, under the specified line:</p>
<p><code># Wrap your base TurboGears 2 application with custom (...)<br />
app = add_auth(app)</code></p>
<p>And voila! Now our classifieds service behaves the same way as before, with its authentication, identification and authorization settings now being controlled by our own code instead of the generated defaults.</p>
<h2>Going beyond with repoze.who and repoze.what</h2>
<p>The topics covered in this article are just the tip of the iceberg. Both Repoze packages are created with extensibility in mind; their core is minimalist but they already have many ready-to-use plugins, not only the repoze.who and repoze.what SQLAlchemy plugins we used here.</p>
<p>There are repoze.who plugins for OpenId, LDAP, <em>.htaccess</em> and RADIUS authentication, as well as a built-in challenger plugin for HTTP authentication &#8212; just to name some of the available plugins. And you can easily create your own plugins, following the patterns described in this article.</p>
<p>Although repoze.what is a relatively new piece of software as of this writing, it has several ready-to-use plugins as well. It has plugins to store the groups and permissions in XML files or .ini files, not only in databases, as well as a plugin called <em>repoze.what-quickstart</em> which allows us to have repoze.who and repoze.what working quickly (that&#8217;s what TurboGears uses to set them up).</p>
<p>Although they were not used in our classifieds service, just mentioned in the beginning, you can have so-called <strong>compound predicates</strong>. Access rules aren&#8217;t always as simple as &#8220;The user must be logged in&#8221; or &#8220;The user belongs to the &#8216;posters&#8217; group&#8221;. For example, in our classified edition mechanism we way want to allow administrators to edit classifieds, even those not posted by themselves; to do so, instead of using our <em>user_is_poster</em> checker alone, we could use it along with the <em>Any</em> and <em>in_group</em> checkers:</p>
<p><code>from repoze.what.predicates import Any, in_group<br />
p = Any(in_group('manager'), user_is_poster())</code></p>
<p>In this article we didn&#8217;t even use half of the built-in repoze.what predicate checkers. A full list is available in the repoze.what manual.</p>
<p>Settings are very flexible. It is even possible to configure repoze.who and repoze.what through .ini files, so that those settings can be adjusted while deploying the application (which would be a replacement for our <em>add_auth()</em> function defined above).</p>
<p>It is also worth noting that both projects are actively developed, well documented, well tested and supported by the Repoze community. As a result, it is most likely that you&#8217;ll have a good experience using them.</p>
<p>repoze.who and repoze.what aim to solve &#8220;the security problem&#8221; we Web developers face so often, and they have proved to be the right choice in many scenarios. The likelihood of them being the right choice for your next Web application is strong, specially now that you&#8217;re familiar with them!</p>
<p><em>To ask questions about <a href="http://docs.repoze.org/who/1.0/">repoze.who</a> and/or <a href="http://what.repoze.org/docs/1.0/">repoze.what</a>, please use the <a href="http://lists.repoze.org/listinfo/repoze-dev">repoze-dev</a> mailing list. For everything else that is related to this article, please leave a comment below.</em></p>
]]></content:encoded>
			<wfw:commentRss>http://gustavonarea.net/blog/posts/repoze-auth/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Getting back on track</title>
		<link>http://gustavonarea.net/blog/posts/getting-back-on-track/</link>
		<comments>http://gustavonarea.net/blog/posts/getting-back-on-track/#comments</comments>
		<pubDate>Tue, 17 Nov 2009 23:38:59 +0000</pubDate>
		<dc:creator>Gustavo</dc:creator>
				<category><![CDATA[Freedomware]]></category>
		<category><![CDATA[Software development]]></category>
		<category><![CDATA[Turbo Gears]]></category>
		<category><![CDATA[Work]]></category>
		<category><![CDATA[2degrees]]></category>
		<category><![CDATA[Django]]></category>
		<category><![CDATA[Madrid]]></category>
		<category><![CDATA[Oxford]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Spain]]></category>
		<category><![CDATA[TurboGears]]></category>
		<category><![CDATA[UK]]></category>
		<category><![CDATA[WSGI]]></category>

		<guid isPermaLink="false">http://gustavonarea.net/?p=285</guid>
		<description><![CDATA[Yes, I&#8217;m alive.
Since the second half of last summer I&#8217;ve been inactive in the Free Software arena. No commits, no emails from me in the last few months which may indicate that the projects are dead. So I wanted to write to let you know that I have no plans to stop maintaining any of [...]]]></description>
			<content:encoded><![CDATA[<p>Yes, I&#8217;m alive.</p>
<p>Since the second half of last summer I&#8217;ve been inactive in the Free Software arena. No commits, no emails from me in the last few months which may indicate that the projects are dead. So I wanted to write to let you know that <strong>I have no plans to stop maintaining any of my projects</strong>. I will start to catch up with all the things I&#8217;ve missed in the projects I normally contribute to and the projects I develop alone.</p>
<p>The reason why you&#8217;d heard nothing from me is that I left Spain to move to Oxford, in order to work at the cool company behind <a title="2Degrees" href="http://www.2degreesnetwork.com">2degreesnetwork.com</a>. The removal was the most time-consuming and stressful thing I&#8217;d ever done, but after one month working here, I&#8217;m happy to say that it was worth it. The atmosphere is just like I thought Web 2.0 companies were, and I am surrounded by nice and talented people. I can&#8217;t be happier.</p>
<p>Well, back to the projects, I had to wait a lot to get access to the Internet at home, but I got it a couple of weeks ago and have been catching up (slowly) with the pending stuff. I still have a huge stack of unanswered emails, for example.</p>
<p>For the last couple of weeks I was working fulltime on <a href="http://what.repoze.org/">repoze.what</a> 1.1 and <a title="The repoze.what Django plugin" href="http://what.repoze.org/docs/plugins/django/">repoze.what-django</a>. I hope to finish the documentation and get the first alpha releases out very soon; the code itself is pretty much ready and, as usual, fully tested. I didn&#8217;t have plans to do a <em>repoze.what</em> 1.1 release anytime soon, but while developing <em>repoze.what-django</em> I found myself implementing something which would be useful outside Django (i.e., ACLs) and thus I decided to move it to <em>repoze.what</em>.</p>
<p>After that, I want to improve the auth documentation in TurboGears 2. <a title="The repoze.what Pylons plugin" href="http://code.gustavonarea.net/repoze.what-pylons/">repoze.what-pylons</a> is the crucial part of the <em>repoze.what</em> integration in TG2 and it&#8217;s fully documented, but duplicating part of those docs won&#8217;t do any harm and adding some tips and tricks would be nice. I started doing that some months ago but never committed it; I have to finish it this time.</p>
<p>Then I&#8217;d like to make <em>repoze.what-pylons</em> take advantage of the new features in <em>repoze.what</em> 1.1, like <em>repoze.what-django</em> already does.</p>
<p>That&#8217;s it for the foreseeable future. Next year I really want to get serious with <a title="Boolean Expressions Interpreter" href="http://code.gustavonarea.net/booleano/">Booleano</a> and <a title="Access Control Lists support for Python" href="https://launchpad.net/pyacl">PyACL</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://gustavonarea.net/blog/posts/getting-back-on-track/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Koren&#8217;s SVD++ Python Implementation</title>
		<link>http://gustavonarea.net/blog/posts/korens-svd-python-implementation/</link>
		<comments>http://gustavonarea.net/blog/posts/korens-svd-python-implementation/#comments</comments>
		<pubDate>Wed, 29 Jul 2009 12:01:30 +0000</pubDate>
		<dc:creator>Gustavo</dc:creator>
				<category><![CDATA[Software development]]></category>
		<category><![CDATA[booleano]]></category>
		<category><![CDATA[Netflix Prize]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Recommender System]]></category>

		<guid isPermaLink="false">http://gustavonarea.net/?p=274</guid>
		<description><![CDATA[I recently had to implement a recommender system for the Netflix Prize. Out of the best known models, I chose Yehuda Koren&#8217;s SVD++ model as published on the paper entitled &#8220;Factorization Meets the Neighborhood: a Multifaceted Collaborative Filtering Model&#8221; (the version that doesn&#8217;t take into account temporal effects; I&#8217;d have implemented the complete model, but [...]]]></description>
			<content:encoded><![CDATA[<p>I recently had to implement a recommender system for the <a href="http://www.netflixprize.com/">Netflix Prize</a>. Out of the best known models, I chose Yehuda Koren&#8217;s SVD++ model as published on the paper entitled &#8220;<a href="http://public.research.att.com/~volinsky/netflix/kdd08koren.pdf">Factorization Meets the Neighborhood: a Multifaceted Collaborative Filtering Model</a>&#8221; (the version that doesn&#8217;t take into account temporal effects; I&#8217;d have implemented the complete model, but couldn&#8217;t due to time constraints).</p>
<p>I named this Python-based project &#8220;wooflix&#8221; and you can download it from <a href="http://code.gustavonarea.net/wooflix.tar.bz">code.gustavonarea.net</a>. It ships with a command-line interface and basic documentation, including the <a href="http://en.wikipedia.org/wiki/Software_design_document">design document</a>.</p>
<p>It&#8217;s the first project, as far as I know, that uses <a href="/blog/posts/announcing-booleano/">Booleano</a>. With it, you can get random movie recommendations and filter them, like this:</p>
<p><code># Get 5 movie recommendations for user #7, at least those published after 2001<br />
wooflix recommendations 7 --max="5" --filter="movie:year > 2001"</code></p>
<p>Keep in mind that I won&#8217;t offer support for it; I&#8217;m publishing because I thought it might be useful for some people, but I have no intentions to work on it in the future.</p>
]]></content:encoded>
			<wfw:commentRss>http://gustavonarea.net/blog/posts/korens-svd-python-implementation/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Announcing Booleano</title>
		<link>http://gustavonarea.net/blog/posts/announcing-booleano/</link>
		<comments>http://gustavonarea.net/blog/posts/announcing-booleano/#comments</comments>
		<pubDate>Fri, 17 Jul 2009 21:26:27 +0000</pubDate>
		<dc:creator>Gustavo</dc:creator>
				<category><![CDATA[Software development]]></category>
		<category><![CDATA[boolean]]></category>
		<category><![CDATA[booleano]]></category>
		<category><![CDATA[natural language processing]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://gustavonarea.net/?p=264</guid>
		<description><![CDATA[I am proud to announce the first alpha release of Booleano, a Python-based interpreter of boolean expressions:

Booleano is an interpreter of boolean expressions, a library to define and run filters available as text (e.g., in a natural language) or in Python code.
In order to handle text-based filters, Booleano ships with a fully-featured parser whose grammar [...]]]></description>
			<content:encoded><![CDATA[<p>I am proud to announce the first alpha release of <a href="http://code.gustavonarea.net/booleano/">Booleano</a>, a Python-based interpreter of boolean expressions:</p>
<blockquote><p>
Booleano is an interpreter of boolean expressions, a library to <strong>define and run filters</strong> available as text (e.g., in a natural language) or in Python code.</p>
<p>In order to handle text-based filters, Booleano ships with a fully-featured parser whose grammar is adaptive: Its properties can be overridden using simple configuration directives.</p>
<p>On the other hand, the library exposes a pythonic API for filters written in pure Python. These filters are particularly useful to build reusable conditions from objects provided by a third party library.
</p></blockquote>
<p>It&#8217;s been designed to address the following use cases:</p>
<ol>
<li>Convert text-based conditions: When you need to turn a condition available as plain text into something else (i.e., another filter).</li>
<li>Evaluate text-based conditions: When you have a condition available as plain text and need to iterate over items in order to filter out those for which the evaluation of the condition is not successful.</li>
<li>Evaluate Python-based conditions: When you have a condition represented by a Python object (nothing to be parsed) and need to iterate over items in order to filter out those for which the evaluation of the condition is not successful.</li>
</ol>
<p>It is a project I found necessary while working on <a href="http://what.repoze.org/">repoze.what 2</a>, which I&#8217;ve been developing for the last few months in my spare time. This release is absolutely usable, but lacks documentation because I needed this release out for a (small) project I need to work on ASAP (it will depend on Booleano). The next release will ship with a nice documentation, I promise.</p>
]]></content:encoded>
			<wfw:commentRss>http://gustavonarea.net/blog/posts/announcing-booleano/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Dell is ashamed of its Ubuntu-powered laptops</title>
		<link>http://gustavonarea.net/blog/posts/dell-ashamed-ubuntu-laptops/</link>
		<comments>http://gustavonarea.net/blog/posts/dell-ashamed-ubuntu-laptops/#comments</comments>
		<pubDate>Thu, 16 Apr 2009 17:54:00 +0000</pubDate>
		<dc:creator>Gustavo</dc:creator>
				<category><![CDATA[Freedomware]]></category>
		<category><![CDATA[Software development]]></category>
		<category><![CDATA[Software engineering]]></category>
		<category><![CDATA[Computers]]></category>
		<category><![CDATA[Dell]]></category>
		<category><![CDATA[ubuntu]]></category>
		<category><![CDATA[Windows]]></category>

		<guid isPermaLink="false">http://gustavonarea.net/?p=222</guid>
		<description><![CDATA[My laptop was slow while running my chain and ball KDE 4, and also got some things broken recently (e.g., battery, screen hinges), so I decided to buy a new one last week before it leaves me stranded. And soon enough I realized that I had two options:

Buy it in a place where every single [...]]]></description>
			<content:encoded><![CDATA[<p>My laptop was slow while running my chain and ball KDE 4, and also got some things broken recently (e.g., battery, screen hinges), so I decided to buy a new one last week before it leaves me stranded. And soon enough I realized that I had two options:</p>
<ul>
<li>Buy it in a place where every single computer ships with Windows, so that I could claim a refund. I didn&#8217;t care about the money: I just wanted to mess with that kind of vendors and file a lawsuit if I didn&#8217;t get it on good terms, to encourage people to do the same thing and thus contribute to do away with the <a title="About Windows" href="http://www.getgnulinux.org/windows/">Windows</a> Tax.</li>
<li>Purchase it from a <a title="GNU/Linux computers" href="http://www.linuxpreloaded.com/">Linux pre-installed</a> vendor, to support them. Even if they pre-installed a freedom-trampling system like Windows, it&#8217;d be good to show them that Freedomware worths it.</li>
</ul>
<p>I liked both options alike, so I based my decision on the computer specs and costs, not on the vendor/manufacturer.</p>
<p>I decided to get a Dell XPS M1330, one of the two Ubuntu-powered computers that I remembered Dell sells in Spain. So I visited <a href="http://www.dell.es/ubuntu">dell.es/ubuntu</a> and was surprised to find just a couple of <a title="What's a netbook?" href="http://en.wikipedia.org/wiki/Netbook">netbooks</a>! Change of plans; now I&#8217;ll have to get it with Windows and claim a refund, I told myself.</p>
<p>So the first step was to get a proof that I was imposed the operating system when I bought the laptop. Sales representatives were available for a chat, so I asked them how could I get a Dell XPS M1330 without Windows. The surprising answer was that <strong>it was available with Ubuntu</strong> and pointed me to <a title="Dell XPS M1330 with Ubuntu pre-installed, in Spain" href="http://configure2.euro.dell.com/dellstore/config.aspx?cs=esdhs1&amp;kc=a&amp;oc=N04X3315&amp;x=0&amp;y=0">configure2.euro.dell.com/dellstore/</a>! Plans changed one more time; back to the original plan, get it with <a href="http://www.getgnulinux.org/switch_to_linux/">Linux</a>.</p>
<p>I obviously asked why it wasn&#8217;t listed on <a href="http://www.dell.es/ubuntu">dell.es/ubuntu</a>. The sales rep said that s/he didn&#8217;t know why and that s/he will forward my query to the relevant department. I bought the laptop with Ubuntu that day and that was it.</p>
<p>Today, out of curiosity, I went to <a href="http://www.dell.es/ubuntu">dell.es/ubuntu</a> and found that it hasn&#8217;t changed! <strong>The link the sales rep provided me with the other day still works but the laptop is not listed</strong>. And the same happens in <a href="http://www.dell.fr/ubuntu">dell.fr/ubuntu</a>, <a href="http://www.dell.co.uk/ubuntu">dell.co.uk/ubuntu</a> and <a href="http://www.dell.de/ubuntu">dell.de/ubuntu</a>, for example.</p>
<p>This can hardly be a mistake. <strong>Why the heck does Dell hide some of the few Linux-powered computers they sell now?</strong> Maybe due to threats from Microsoft? After all, <a title="Microsoft and its monopolistic practices" href="http://www.google.com/search?q=%2Bmicrosoft%2Bmonopolistic%2Bpractices">it&#8217;s well-know for its monopolistic practices</a>.</p>
<p><strong>PS (April 18th @ 14:00 UTC):</strong> The link above to <a href="http://configure2.euro.dell.com/dellstore/config.aspx?cs=esdhs1&amp;kc=a&amp;oc=N04X3315&amp;x=0&amp;y=0">configure2.euro.dell.com/dellstore/</a> doesn&#8217;t work at times today, so here&#8217;s an screenshot if it doesn&#8217;t work for you:</p>
<p><a href="http://gustavonarea.net/uploads/ubuntu-xps-configurator.png"><img class="aligncenter size-thumbnail wp-image-234" title="Dell XPS with Ubuntu in Dell's configuration service" src="http://gustavonarea.net/uploads/ubuntu-xps-configurator.png" alt="" width="150" height="92" /></a></p>
<p><strong>PS (April 19th @ 18:30 UTC):</strong> This is an screenshot of the random error I warned about yesterday (which I took just in case), before reaching Digg.com&#8217;s front-page:</p>
<p><a href="http://gustavonarea.net/uploads/dell-ubuntu-error-april18.png"><img class="aligncenter size-thumbnail wp-image-239" title="Error before hitting Digg.com's front-page" src="http://gustavonarea.net/uploads/dell-ubuntu-error-april18.png" alt="" width="150" height="92" /></a></p>
<p>Now, almost 20 hours after reaching Digg&#8217;s front-page, the link no longer works (not even at times, as yesterday) and a better formatted page is displayed instead:</p>
<p><a href="http://gustavonarea.net/uploads/dell-ubuntu-error-april19.png"><img class="aligncenter size-thumbnail wp-image-240" title="Error after hitting Digg.com's front-page" src="http://gustavonarea.net/uploads/dell-ubuntu-error-april19.png" alt="" width="150" height="92" /></a></p>
<p>I don&#8217;t know if the different error pages actually mean something, but my point is that the link is now dead.</p>
]]></content:encoded>
			<wfw:commentRss>http://gustavonarea.net/blog/posts/dell-ashamed-ubuntu-laptops/feed/</wfw:commentRss>
		<slash:comments>45</slash:comments>
		</item>
		<item>
		<title>The repoze.who LDAP plugin will be an official plugin</title>
		<link>http://gustavonarea.net/blog/posts/the-repozewho-ldap-plugin-will-be-an-official-plugin/</link>
		<comments>http://gustavonarea.net/blog/posts/the-repozewho-ldap-plugin-will-be-an-official-plugin/#comments</comments>
		<pubDate>Mon, 27 Oct 2008 00:15:01 +0000</pubDate>
		<dc:creator>Gustavo</dc:creator>
				<category><![CDATA[repoze.who.plugins.ldap]]></category>
		<category><![CDATA[LDAP]]></category>
		<category><![CDATA[Repoze]]></category>
		<category><![CDATA[repoze.who]]></category>

		<guid isPermaLink="false">http://gustavonarea.net/?p=145</guid>
		<description><![CDATA[Some weeks ago I was invited to make repoze.who.plugins.ldap an official repoze.who plugin, which means that:

The license will change. It will use Repoze&#8217;s.
The development tools will be migrated from Launchpad (bug tracker, repository, etc).
The LDAP plugin&#8217;s documentation will be included into repoze.who&#8217;s.
It will be maintained by Repoze commiters, and I&#8217;m one of them.

I&#8217;ve not started [...]]]></description>
			<content:encoded><![CDATA[<p>Some weeks ago I was invited to make <a href="http://code.gustavonarea.net/repoze.who.plugins.ldap/">repoze.who.plugins.ldap</a> an official repoze.who plugin, which means that:</p>
<ul>
<li>The license will change. It will use <a href="http://repoze.org/license.html">Repoze&#8217;s</a>.</li>
<li>The development tools will be migrated from <a href="https://launchpad.net/repoze.who.plugins.ldap">Launchpad</a> (bug tracker, repository, etc).</li>
<li>The LDAP plugin&#8217;s documentation will be included into <a href="http://static.repoze.org/whodocs/">repoze.who&#8217;s</a>.</li>
<li>It will be maintained by Repoze commiters, and I&#8217;m one of them.</li>
</ul>
<p>I&#8217;ve not started the migration, but I hope to start in a few days.</p>
]]></content:encoded>
			<wfw:commentRss>http://gustavonarea.net/blog/posts/the-repozewho-ldap-plugin-will-be-an-official-plugin/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Enable LDAP authentication in your WSGI applications!</title>
		<link>http://gustavonarea.net/blog/posts/enable-ldap-authentication-in-your-wsgi-applications/</link>
		<comments>http://gustavonarea.net/blog/posts/enable-ldap-authentication-in-your-wsgi-applications/#comments</comments>
		<pubDate>Mon, 08 Sep 2008 23:50:15 +0000</pubDate>
		<dc:creator>Gustavo</dc:creator>
				<category><![CDATA[Software development]]></category>
		<category><![CDATA[repoze.who.plugins.ldap]]></category>
		<category><![CDATA[LDAP]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[repoze.who]]></category>
		<category><![CDATA[WSGI]]></category>

		<guid isPermaLink="false">http://gustavonarea.net/?p=119</guid>
		<description><![CDATA[repoze.who.plugins.ldap is an straightforward yet powerful solution to enable LDAP authentication in your WSGI application. It enables you to have LDAP authentication working in your new or existing applications, in few minutes and with few lines of code!
It&#8217;s a plugin for the repoze.who framework, featuring not only an LDAP authenticator, but also related utilities. It&#8217;s [...]]]></description>
			<content:encoded><![CDATA[<p><a title="LDAP Authentication for WSGI Applications" href="http://code.gustavonarea.net/repoze.who.plugins.ldap/">repoze.who.plugins.ldap</a> is an straightforward yet powerful solution to enable <a title="Lightweight Directory Access Protocol" href="http://en.wikipedia.org/wiki/Lightweight_Directory_Access_Protocol">LDAP</a> authentication in your WSGI application. It enables you to have LDAP authentication working in your new or existing applications, in few minutes and with few lines of code!</p>
<p>It&#8217;s a plugin for the <a title="WSGI Authentication Middleware" href="http://static.repoze.org/whodocs/">repoze.who</a> framework, featuring not only an LDAP authenticator, but also related utilities. It&#8217;s a fully documented project which also ships with a working demo application, so it&#8217;d be hard for you to get stuck.</p>
<p>I wrote this plugin in order to enable LDAP authentication in <a title="Animador" href="http://tracker.gnulinuxmatters.org/wiki/Animador">Animador</a>. And in fact, it&#8217;s the first application that uses the plugin.</p>
<p>The latest version is 1.0, and you&#8217;re highly encouraged to play with it and give feedback!</p>
<p><a href="http://code.gustavonarea.net/repoze.who.plugins.ldap/">Visit its website for more information!</a></p>
]]></content:encoded>
			<wfw:commentRss>http://gustavonarea.net/blog/posts/enable-ldap-authentication-in-your-wsgi-applications/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Joining the development of TurboGears</title>
		<link>http://gustavonarea.net/blog/posts/joining-the-development-of-turbogears/</link>
		<comments>http://gustavonarea.net/blog/posts/joining-the-development-of-turbogears/#comments</comments>
		<pubDate>Mon, 18 Aug 2008 17:23:56 +0000</pubDate>
		<dc:creator>Gustavo</dc:creator>
				<category><![CDATA[Freedomware]]></category>
		<category><![CDATA[GNU/Linux Matters]]></category>
		<category><![CDATA[Software development]]></category>
		<category><![CDATA[Turbo Gears]]></category>
		<category><![CDATA[animador]]></category>
		<category><![CDATA[OpenId]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[TurboGears]]></category>

		<guid isPermaLink="false">http://gustavonarea.net/?p=91</guid>
		<description><![CDATA[I&#8217;ve had the great pleasure of being invited by the TurboGears team to become an official committer for the great TurboGears framework.
This is because I&#8217;ve been contributing patches for TurboGears 2 and other packages used by Animador (a TurboGears 2 application), since I started its development, in order to fix bugs and/or add new features [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve had the great pleasure of being invited by the <a href="http://en.wikipedia.org/wiki/TurboGears">TurboGears</a> team to become an official <a title="What's a committer?" href="http://en.wikipedia.org/wiki/Committer">committer</a> for <a href="http://turbogears.org/about/">the great TurboGears framework</a>.</p>
<p>This is because I&#8217;ve been contributing patches for TurboGears 2 and other packages used by <a href="http://tracker.gnulinuxmatters.org/wiki/Animador">Animador</a> (a <a href="http://turbogears.org/2.0/docs/index.html">TurboGears 2</a> application), since I started its development, in order to fix bugs and/or add new features that I want in Animador. So now I can apply my changes by myself! <img src='http://gustavonarea.net/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> </p>
<p>And stay tunned, because very soon it&#8217;s going to be very easy to add <a href="http://openid.net/">OpenId</a> support to any WSGI application by means of <a href="http://tracker.gnulinuxmatters.org/ticket/264">a plugin for the framework-independent <em>repoze.who</em> package</a>!</p>
]]></content:encoded>
			<wfw:commentRss>http://gustavonarea.net/blog/posts/joining-the-development-of-turbogears/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
	</channel>
</rss>
