<?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; HOWTOs</title>
	<atom:link href="http://gustavonarea.net/blog/topics/howtos/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>Backing up and restoring your Subversion repository</title>
		<link>http://gustavonarea.net/blog/posts/backing-up-and-restoring-your-subversion-repository/</link>
		<comments>http://gustavonarea.net/blog/posts/backing-up-and-restoring-your-subversion-repository/#comments</comments>
		<pubDate>Fri, 30 May 2008 09:03:28 +0000</pubDate>
		<dc:creator>Gustavo</dc:creator>
				<category><![CDATA[HOWTOs]]></category>
		<category><![CDATA[Subversion]]></category>

		<guid isPermaLink="false">http://gustavonarea.net/?p=51</guid>
		<description><![CDATA[Most tutorials around show how to backup your Subversion repository:
svnadmin dump /path/to/your/repository &#62; ./repository.dump
And how to restore it:
mkdir /new/path/to/your/repository
svnadmin create /new/path/to/your/repository
svnadmin load /new/path/to/your/repository &#60; ./repository.dump
But don&#8217;t tell you what to do to solve the problem that will arise when you try to restore it:
svnadmin: Dump stream contains a malformed header (with no ':') at '* [...]]]></description>
			<content:encoded><![CDATA[<p>Most tutorials around show how to backup your Subversion repository:</p>
<p><code>svnadmin dump /path/to/your/repository &gt; ./repository.dump</code></p>
<p>And how to restore it:</p>
<p><code>mkdir /new/path/to/your/repository<br />
svnadmin create /new/path/to/your/repository<br />
svnadmin load /new/path/to/your/repository &lt; ./repository.dump</code></p>
<p>But don&#8217;t tell you what to do to solve the problem that will arise when you try to restore it:</p>
<p><code>svnadmin: Dump stream contains a malformed header (with no ':') at '* Dumped revision 0.'</code></p>
<p>The solution is too simple; just run:</p>
<p><code>grep --binary-files=text -v '^* Dumped revision' ./repository.dump > ./repository.clear-dump</code></p>
<p>Now you should be able to restore your subversion repository with no problems:</p>
<p><code>svnadmin load /new/path/to/your/repository &lt; ./repository.clear-dump</code></p>
<p>PS: I don&#8217;t use <em><a href="http://svnbook.red-bean.com/en/1.0/re33.html">hotcopy</a></em> because I prefer to have a single file for these backups.</p>
]]></content:encoded>
			<wfw:commentRss>http://gustavonarea.net/blog/posts/backing-up-and-restoring-your-subversion-repository/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Installing Pootle on Debian Etch, The Easiest Way</title>
		<link>http://gustavonarea.net/blog/posts/installing-pootle-on-debian-etch-the-easiest-way/</link>
		<comments>http://gustavonarea.net/blog/posts/installing-pootle-on-debian-etch-the-easiest-way/#comments</comments>
		<pubDate>Wed, 21 May 2008 15:34:52 +0000</pubDate>
		<dc:creator>Gustavo</dc:creator>
				<category><![CDATA[HOWTOs]]></category>
		<category><![CDATA[Debian]]></category>
		<category><![CDATA[Internationalization]]></category>
		<category><![CDATA[Pootle]]></category>

		<guid isPermaLink="false">http://gustavonarea.net/?p=41</guid>
		<description><![CDATA[Pootle is a user-friendly web application for online translation of contents. It&#8217;s used by organizations like Creative Commons, OpenOffice.org and GNU/Linux Matters.
I&#8217;m going to show you how to install it the easiest way, from my experience with the GLM Translation Service under Debian 4.0 (this guide might also work under Ubuntu, though). I&#8217;ll use the [...]]]></description>
			<content:encoded><![CDATA[<p><a title="A user-friendly web portal that makes the translation process so much simpler" href="http://translate.sourceforge.net/wiki/pootle/index">Pootle</a> is a user-friendly web application for online translation of contents. It&#8217;s used by organizations like <a href="http://translate.creativecommons.org/">Creative Commons</a>, <a href="http://www.sunvirtuallab.com:32300/">OpenOffice.org</a> and <a title="GNU/Linux Matters!" href="http://translate.gnulinuxmatters.org/">GNU/Linux Matters</a>.</p>
<p>I&#8217;m going to show you how to install it the easiest way, from my experience with the <a title="GNU/Linux Matters!" href="http://translate.gnulinuxmatters.org/">GLM Translation Service</a> under Debian 4.0 (this guide might also work under Ubuntu, though). I&#8217;ll use the <a title="Sudo Configuration in Debian" href="http://www.debianhelp.co.uk/sudo.htm">sudo</a> utility, so I assume that you&#8217;ve already installed and configured sudo accordingly for your user.</p>
<h2>Installing dependencies</h2>
<p>Some of the Pootle&#8217;s dependencies are available in Debian repositories, so we may install them by running:<br />
<code>sudo aptitude install python-dev python-pysqlite2 python-celementtree subversion bzip2 g++ curl</code></p>
<p>We&#8217;re now going to install the other software packages required by Pootle. First, select the directory where you want to download and compile your software, for example:<br />
<code>cd /usr/src</code></p>
<p>To get the source code of the packages and extract them, run the following commands:<br />
<code>curl http://www.kid-templating.org/dist/0.9.6/kid-0.9.6.tar.gz | tar xzv<br />
curl http://dfn.dl.sourceforge.net/sourceforge/translate/translate-toolkit-1.1.1.tar.bz2 | tar xjv<br />
curl http://garr.dl.sourceforge.net/sourceforge/translate/Pootle-1.1.0.tar.bz2 | tar xjv<br />
curl http://jtoolkit.sourceforge.net/snapshots/jToolkit-0.7.8/jToolkit-0.7.8.tar.gz | tar xzv<br />
</code></p>
<p>Installing the applications:<br />
<code>sudo python translate-toolkit-1.1.1/setup.py install<br />
sudo python kid-0.9.6/setup.py install<br />
sudo Pootle-1.1.0/pootlesetup.py install<br />
sudo jToolkit-0.7.8/jToolkitSetup.py install<br />
</code></p>
<h3>Do not install PyLucene</h3>
<p>Pootle is supposed to work better with PyLucene, but if you (like me) hate headaches, you won&#8217;t install PyLucene on Debian Etch. If you try to install it, you&#8217;d get this error:<br />
<code>install: cannot stat `/usr/lib/security/libgcj.security': No such file or directory<br />
make: *** [install] Error 1</code></p>
<p>There&#8217;s nothing you could install that would create that file; don&#8217;t you even try to create it as an empty file: You&#8217;d be able to install PyLucene, but Pootle won&#8217;t work and you&#8217;d get this error when you&#8217;ll try to run it:<br />
<code>GC Warning: Repeated allocation of very large block (appr. size 65536):<br />
May lead to memory leak and poor performance.</code></p>
<p>If it&#8217;s too late, and you&#8217;ve already installed PyLucene with <em>libgcj.security</em> as an empty file (as suggested in some mailing-lists), you may fix it by running the following commands:<br />
<code>sudo mv /usr/lib/python2.4/site-packages/_PyLucene.so /usr/lib/python2.4/site-packages/_PyLucene.so.old<br />
sudo mv /usr/lib/python2.4/site-packages/PyLucene.py /usr/lib/python2.4/site-packages/PyLucene.py.old<br />
sudo mv /usr/lib/python2.4/site-packages/PyLucene.pyc /usr/lib/python2.4/site-packages/PyLucene.pyc.old</code></p>
<h3>Don&#8217;t you have a mail server?</h3>
<p>If your mail server is not in the same host as Pootle, you&#8217;ll need to install one. Just run the command below to install <a href="http://en.wikipedia.org/wiki/Postfix_%28software%29">Postfix</a> and then select &#8220;Internet website&#8221; when asked about what you&#8217;ll use postfix for:<br />
<code>sudo apt-get install postfix</code></p>
<h2>Configuring Pootle</h2>
<p>To make it easy for you to configure and play with Pootle, I suggest you create softlinks to Pootle&#8217;s configuration files in a folder like <em>/etc/pootle</em>:<br />
<code>sudo mkdir /etc/pootle<br />
sudo ln /usr/lib/python2.4/site-packages/Pootle/pootle.prefs -s /etc/pootle/pootle.conf<br />
sudo ln /usr/lib/python2.4/site-packages/Pootle/users.prefs -s /etc/pootle/users.conf<br />
sudo ln /usr/lib/python2.4/site-packages/Pootle/html -s /etc/pootle/html<br />
sudo ln /usr/lib/python2.4/site-packages/Pootle/templates -s /etc/pootle/templates</code></p>
<p>This way, any file you might ever need to edit will be in /etc/pootle.</p>
<p>Now let&#8217;s edit the main configuration file in Pootle: <code>sudo nano /etc/pootle/pootle.conf</code></p>
<p>It&#8217;s well-documented, so I won&#8217;t talk a lot about it. I just suggest you only set the following parameters accordingly:</p>
<ul>
<li><em>description</em>: Describe your website powered by Pootle, with a text aimed at (potential) translators.</li>
<li><em>fromaddress</em>.</li>
<li><em>supportaddress</em>: The email address for translators to make questions. This might be a mailing list.</li>
<li><em>defaultrights</em>: I suggest you set it to <code>defaultrights = "view, suggest, archive, pocompile, translate, commit"</code></li>
<li><em>podirectory</em>: It&#8217;s the path to your translation files. For this HOWTO I&#8217;ll use <em>/var/translations</em>.</li>
</ul>
<h2>Your <em>pootle</em> user</h2>
<p>I suggest you create a user for running <em>pootle</em>:<br />
<code>sudo adduser --disabled-password --disabled-login pootle</code></p>
<h2>Setting up service scripts for Pootle</h2>
<p>Create the file <em>/etc/init.d/pootle</em> as root with the following contents:<br />
<code>#!/bin/bash<br />
# /etc/init.d/pootle: start and stop the Pootle Server<br />
# Pootle runs as user -pootle- via sudo<br />
# This script is based on the one published here http://translate.sourceforge.net/wiki/pootle/service_scripts<br />
PREFIX=/usr/local/pootle<br />
test -x /usr/bin/PootleServer || exit 0<br />
. /lib/lsb/init-functions<br />
case "$1" in<br />
start)<br />
log_begin_msg "Starting Pootle Server..."<br />
/usr/bin/sudo -u pootle $PREFIX/start_pootle &amp;<br />
log_end_msg 0<br />
;;<br />
stop)<br />
log_begin_msg "Stopping Pootle Server..."<br />
$PREFIX/stop_pootle &gt;/dev/null 2&gt;&amp;1<br />
log_end_msg 0<br />
;;<br />
restart)<br />
log_begin_msg "Stopping Pootle Server..."<br />
$PREFIX/stop_pootle &gt;/dev/null 2&gt;&amp;1<br />
log_end_msg 0<br />
log_begin_msg "Starting Pootle Server..."<br />
sudo -u pootle $PREFIX/start_pootle &amp;<br />
log_end_msg 0<br />
;;<br />
status)<br />
$PREFIX/status_pootle<br />
;;<br />
*)<br />
log_success_msg "Usage: /etc/init.d/pootle {start|stop|restart|status}"<br />
exit 1<br />
esac<br />
exit 0</code></p>
<p>Now let&#8217;s create, as root, the auxiliary scripts required by the script above:</p>
<h3>/usr/local/pootle/start_pootle</h3>
<p><code>#!/bin/bash<br />
# $PREFIX/start_pootle<br />
# Start script in port 8080, separates errors from normal logs<br />
# based on a script published here: http://translate.sourceforge.net/wiki/pootle/service_scripts<br />
export HOME="/home/pootle"<br />
/usr/bin/PootleServer -p 8080 $@ &gt;&gt; /var/log/pootle/`date "+%F"` 2&gt;&gt; /var/log/pootle/`date "+%F"`.err</code></p>
<h3>/usr/local/pootle/stop_pootle</h3>
<p><code><br />
#!/bin/bash<br />
# Stop script $PREFIX/stop_pootle<br />
# based on a script published here: http://translate.sourceforge.net/wiki/pootle/service_scripts<br />
echo "Killing Pootle"<br />
pidpootle=`ps -ef |grep PootleServer |grep python | awk '{print $2}'`<br />
pidpootle2=`ps -ef |grep start_pootle |grep bin | awk '{print $2}'`<br />
kill $pidpootle2 &gt;/dev/null 2&gt;&amp;1<br />
kill $pidpootle &gt;/dev/null 2&gt;&amp;1</code></p>
<h3>/usr/local/pootle/status_pootle</h3>
<p><code>#!/bin/bash<br />
# Pootle Status $PREFIX/status_pootle<br />
# based on a script published here: http://translate.sourceforge.net/wiki/pootle/service_scripts<br />
pidpootle=`ps -ef |grep PootleServer |grep python | awk '{print $2}'`<br />
if [ "$pidpootle" != "" ]<br />
then<br />
echo "Pootle Server running in pid ($pidpootle)"<br />
else<br />
echo "Pootle Server is not running"<br />
fi</code></p>
<h3>Make them work!</h3>
<p>Now we have to make these scripts <em>useful</em>:<br />
<code>sudo chmod +x /etc/init.d/pootle<br />
sudo chmod +x /usr/local/pootle/s*<br />
sudo update-rc.d pootle</code></p>
<h2>Configuring Pootle to use your repository</h2>
<p>Pootle supports several <a href="http://en.wikipedia.org/wiki/Revision_control">VCSs</a>, but I&#8217;ll only teach you how to setup a Subversion working copy that Pootle may update and commit. If you don&#8217;t use a version control system, I recommend you do so. If you&#8217;re sure you don&#8217;t need it, you may skip this section. If you&#8217;re using another VCS, you&#8217;ll need to go to the Pootle&#8217;s website to <a title="Version control in Pootle" href="http://translate.sourceforge.net/wiki/pootle/version_control">learn how to configure it with Pootle</a>.</p>
<h3>Setup your working copy</h3>
<p>Let&#8217;s create the working copy at, say, <em>/var/translations</em>. To do that in GNU/Linux Matters, we would run:<br />
<code>sudo svn co https://svn.gnulinuxmatters.org:81/i18n /var/translations</code><br />
You should replace the URL by yours.<br />
And don&#8217;t forget to set the <em>pootle</em> user as the owner:<br />
<code>sudo chown pootle -R /var/translations</code></p>
<h3>Create basic cronjobs</h3>
<p>We should run from time to time two scripts:</p>
<ul>
<li>One to commit the changes made by translators; ideally, these changes would be committed by translators themselves, but from my experience you should not count on that (most of them forget about it or just don&#8217;t know they can do that).</li>
<li>Another to update your PO templates from the repository.</li>
</ul>
<p>So we&#8217;re going to ask our <em>pootle</em> user to do this by creating cronjobs. First, <em>become pootle</em>:<br />
<code>sudo su - pootle</code><br />
Then run <code>crontab -e</code> and add the following two lines:<br />
<code>*/15 * * * * /usr/bin/svn update /var/translations &gt;&gt; /dev/null 2&gt;&gt; /home/pootle/update.err<br />
0 0,8,16 * * * /usr/bin/svn commit -m "Uncommitted translations in the last 8 hours" /var/translations &gt;&gt; /dev/null 2&gt;&gt; /home/pootle/commit.err</code></p>
<h2>Pootle is running on port 8080</h2>
<p>For security reasons, if you followed this HOWTO you&#8217;ll be running as the <em>pootle</em> user, not as root. And due to a UNIX misfeature, only root may open ports below 1024. So, unless you love appending the port number to your URLs, and if port 80 is available on your system, you&#8217;d <a title="Proxying Pootle through Apache" href="http://translate.sourceforge.net/wiki/pootle/apache">proxy Pootle through a webserver</a>.</p>
<p>The proxy trick is recommended, but if you cannot do that or just don&#8217;t want, you can use iptables to redirect traffic from port 8080 to 80. My suggestion would be to use the next iptables ruleset:<br />
<code># Generated by iptables-save v1.3.6 on Thu Jan 24 16:40:59 2008<br />
*nat<br />
 <img src='http://gustavonarea.net/wp-includes/images/smilies/icon_razz.gif' alt=':P' class='wp-smiley' /> REROUTING ACCEPT [0:0]<br />
 <img src='http://gustavonarea.net/wp-includes/images/smilies/icon_razz.gif' alt=':P' class='wp-smiley' /> OSTROUTING ACCEPT [0:0]<br />
:OUTPUT ACCEPT [0:0]<br />
-A PREROUTING -p tcp -m tcp --dport 80 -j REDIRECT --to-ports 8080<br />
COMMIT<br />
# Completed on Thu Jan 24 16:40:59 2008<br />
# Generated by iptables-save v1.3.6 on Thu Jan 24 16:40:59 2008<br />
*filter<br />
:INPUT ACCEPT [0:0]<br />
:FORWARD ACCEPT [0:0]<br />
:OUTPUT ACCEPT [495:60715]<br />
-A INPUT -i lo -j ACCEPT<br />
-A INPUT -m state --state INVALID -j DROP<br />
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT<br />
-A INPUT -p tcp -m tcp --dport 22 -j ACCEPT<br />
-A INPUT -p tcp -m tcp --dport 8080 -j ACCEPT<br />
-A INPUT -p icmp -m icmp --icmp-type 8 -j ACCEPT<br />
-A INPUT -m limit --limit 5/min -j LOG --log-prefix "iptables denied: " --log-level 7<br />
-A INPUT -p tcp -j REJECT --reject-with tcp-reset<br />
-A INPUT -p udp -j REJECT --reject-with icmp-port-unreachable<br />
-A INPUT -j REJECT --reject-with icmp-proto-unreachable<br />
-A FORWARD -j REJECT --reject-with icmp-port-unreachable<br />
COMMIT<br />
# Completed on Thu Jan 24 16:40:59 2008</code></p>
<p>For more information about this basic iptables-based firewall, you may read my <a href="/blog/posts/setting-up-your-first-server/">Setting up your first server</a> HOWTO.</p>
<h2>Criticism? Suggestions? Complaints?</h2>
<p>Leave a comment!</p>
]]></content:encoded>
			<wfw:commentRss>http://gustavonarea.net/blog/posts/installing-pootle-on-debian-etch-the-easiest-way/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>The only screencasting software that doesn&#8217;t crash in Ubuntu Hardy</title>
		<link>http://gustavonarea.net/blog/posts/the-only-screencasting-software-that-doesnt-crash-in-ubuntu-hardy/</link>
		<comments>http://gustavonarea.net/blog/posts/the-only-screencasting-software-that-doesnt-crash-in-ubuntu-hardy/#comments</comments>
		<pubDate>Mon, 19 May 2008 17:07:59 +0000</pubDate>
		<dc:creator>Gustavo</dc:creator>
				<category><![CDATA[GNU/Linux Matters]]></category>
		<category><![CDATA[HOWTOs]]></category>
		<category><![CDATA[Debian]]></category>
		<category><![CDATA[glm]]></category>
		<category><![CDATA[gutsy]]></category>
		<category><![CDATA[hardy]]></category>
		<category><![CDATA[istanbul]]></category>
		<category><![CDATA[recordmydesktop]]></category>
		<category><![CDATA[screencast]]></category>
		<category><![CDATA[ubuntu]]></category>
		<category><![CDATA[xvidcap]]></category>

		<guid isPermaLink="false">http://gustavonarea.net/?p=48</guid>
		<description><![CDATA[I&#8217;ve spent a lot of time trying to record some screencasts for GLM, but every screencasting software I tried crashed under Ubuntu Hardy (and Gutsy too):

XVidCap: The workaround is supposed to be disabling sound, but didn&#8217;t work.
RecordMyDesktop (plus its two front-ends): Crashes with no error message. From time to time I was able to record [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve spent a lot of time trying to <a href="https://tracker.gnulinuxmatters.org/ticket/139">record some screencasts for GLM</a>, but every screencasting software I tried crashed under Ubuntu Hardy (and Gutsy too):</p>
<ul>
<li>XVidCap: The workaround is supposed to be disabling sound, but didn&#8217;t work.</li>
<li>RecordMyDesktop (plus its two front-ends): Crashes with no error message. From time to time I was able to record videos.</li>
<li>Istanbul: Crashes if you select a window to be recorded.</li>
</ul>
<p>So, the only solution is to install a newer version of Istanbul for Debian:<br />
<code>http://ftp.fr.debian.org/debian/pool/main/i/istanbul/istanbul_0.2.2-4_i386.deb</code><br />
or, if you have a 64-bit box, <code>http://ftp.fr.debian.org/debian/pool/main/i/istanbul/istanbul_0.2.2-4_amd64.deb</code></p>
<p>After making the appropriate bug reports, I can finally start recording the screencasts with Istanbul!</p>
<p>PS: Forget about the above. That Debian package doesn&#8217;t work either. I&#8217;m installing KDE4 to check whether I can record the screencast with it; I think I saw such an option when I tried it out.</p>
<p>PPS: The KDE4 built-in screencasting component uses a weird format (cps?) which cannot be opened with VLC, Mplayer nor Kaffeine (<a href="http://ubuntuforums.org/showpost.php?p=4717105&amp;postcount=2">but there&#8217;s a dirty workaround</a>). Anyways, it records the whole screen and I just want to record a single window.</p>
<p>PPPS: <a href="http://ubuntuforums.org/showthread.php?p=5016504">I&#8217;m not alone</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://gustavonarea.net/blog/posts/the-only-screencasting-software-that-doesnt-crash-in-ubuntu-hardy/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Setting up your first server</title>
		<link>http://gustavonarea.net/blog/posts/setting-up-your-first-server/</link>
		<comments>http://gustavonarea.net/blog/posts/setting-up-your-first-server/#comments</comments>
		<pubDate>Mon, 07 Apr 2008 14:55:20 +0000</pubDate>
		<dc:creator>Gustavo</dc:creator>
				<category><![CDATA[HOWTOs]]></category>
		<category><![CDATA[Debian]]></category>
		<category><![CDATA[firewall]]></category>
		<category><![CDATA[gnu/linux]]></category>
		<category><![CDATA[iptables]]></category>
		<category><![CDATA[ssh]]></category>

		<guid isPermaLink="false">http://gustavonarea.net/?p=42</guid>
		<description><![CDATA[If you&#8217;re new to server management and use the terminal on GNU/Linux from time to time, this guide will hopefully come in handy for you to get started with your own server.
Unless you have good reasons to use another GNU/Linux distribution, I recommend you to install Debian. It has a lot of ready-to-install applications, is [...]]]></description>
			<content:encoded><![CDATA[<p>If you&#8217;re new to server management and use the terminal on <a href="http://www.getgnulinux.org/">GNU/Linux</a> from time to time, this guide will hopefully come in handy for you to get started with your own server.</p>
<p>Unless you have good reasons to use another <a href="http://www.getgnulinux.org/">GNU/Linux</a> distribution, I recommend you to install <a href="http://www.debian.org">Debian</a>. It has a lot of ready-to-install applications, is very stable and it&#8217;s perhaps the distribution with more tutorials around.</p>
<p>Please notice that this is a <em>very basic</em> tutorial and has only been tested on Debian.</p>
<h2>Connect to your server</h2>
<p>First of all, log in as root:<br />
<code>ssh root@123.123.123.123 # where 123.123.123.123 is your server's IP address </code></p>
<p>Some hosting providers disable ssh root access, so you will need to replace <em>root</em> by your user name. If this is the case, after you log in you should become <em>root</em>:</p>
<p><code>su -</code></p>
<h2>Update your system</h2>
<p><code>aptitude update<br />
aptitude upgrade<br />
aptitude dist-upgrade</code></p>
<h2>Add your user</h2>
<p>If your hosting provider disables root access, then you should skip this step.</p>
<p><code>adduser emacs</code></p>
<p>Replace emacs by <a href="http://en.wikipedia.org/wiki/Vi">VI</a> <a href="http://en.wikipedia.org/wiki/Vi">VI</a> <a href="http://en.wikipedia.org/wiki/Vi">VI</a> if you don&#8217;t believe in <a href="http://www.stallman.org/saint.html">Saint IGNUcius</a>.</p>
<h2>Sudo setup</h2>
<p><a href="http://en.wikipedia.org/wiki/Sudo">sudo</a> is a very useful utility, and I recommend you to use it.</p>
<p>First, let&#8217;s install it:</p>
<p><code>aptitude install sudo</code></p>
<p>Then, we add your user to the list of sudoers, by running <code>visudo</code> and then adding the following line at the end of the file <code>emacs ALL=(ALL) ALL</code>.</p>
<p>Now you become yourself:</p>
<p><code>su emacs -</code></p>
<h2>Shared key ssh authentication</h2>
<p>At this point you should use shared key ssh authentication, but for that there&#8217;s <a href="http://ammonlauritzen.com/blog/2006/04/16/shared_key_ssh_authentication/">a great tutorial at ammonlauritzen.com</a>.</p>
<h2>Configuring the SSH daemon</h2>
<p>Open /etc/ssh/sshd_config with your favorite text editor, say:</p>
<p><code>sudo nano /etc/ssh/sshd_config</code></p>
<p>And make sure the following lines are set this way, if not, add or modify them accordingly:<br />
<code>PermitRootLogin no<br />
PasswordAuthentication no<br />
X11Forwarding no<br />
UsePAM no<br />
AllowUsers emacs                      # separate two or more usernames by spaces</code></p>
<p>Finally, apply your modifications:<br />
<code>sudo /etc/init.d/ssh reload</code></p>
<p>Don&#8217;t log out yet, we need to check that you will be able to access your server via ssh (this is, that you didn&#8217;t break anything on the /etc/ssh/sshd_config file). To check if everything is OK, try to log in:<br />
<code>ssh emacs@123.123.123.123</code></p>
<p>If you&#8217;re able to access, then it&#8217;s well configured and you may close the second session. If not, then you should check your modifications and try again.</p>
<h2>Setting up a basic firewall</h2>
<p>We are going to setup a very basic firewall with the powerful <a href="http://www.netfilter.org/">netfilter/iptables</a>. For this step you need to be root:<br />
<code>sudo -s</code></p>
<p>First, store the current iptables rules, in case something goes wrong with ours:<br />
<code>iptables-save &gt; /etc/iptables.conf.old</code></p>
<p>Now, create the file <em>/etc/iptables.conf</em> and add the following contents:<br />
<code># boring stuff for someone new to server administration<br />
*filter<br />
:INPUT ACCEPT [0:0]<br />
:FORWARD ACCEPT [0:0]<br />
:OUTPUT ACCEPT [495:60715]<br />
-A INPUT -i lo -j ACCEPT<br />
-A INPUT -m state --state INVALID -j DROP<br />
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT<br />
# this is the port used by the SSH daemon<br />
-A INPUT -p tcp -m tcp --dport 22 -j ACCEPT<br />
-A INPUT -p icmp -m icmp --icmp-type 8 -j ACCEPT<br />
-A INPUT -m limit --limit 5/min -j LOG --log-prefix "iptables denied: " --log-level 7<br />
-A INPUT -p tcp -j REJECT --reject-with tcp-reset<br />
-A INPUT -p udp -j REJECT --reject-with icmp-port-unreachable<br />
-A INPUT -j REJECT --reject-with icmp-proto-unreachable<br />
-A FORWARD -j REJECT --reject-with icmp-port-unreachable<br />
COMMIT</code></p>
<p>Please pay attention to this line:<br />
<code>-A INPUT -p tcp -m tcp --dport 22 -j ACCEPT</code></p>
<p>You should use a line similar for every open port that you want to be accessible from the Internet. This is, if you have a webserver, you should copy that line but replace &#8220;22&#8243; by &#8220;80&#8243; (or any other port):<br />
<code>-A INPUT -p tcp -m tcp --dport 80 -j ACCEPT</code></p>
<p>This is how you &#8220;enable&#8221; ports.</p>
<p>Then we load the configuration (and <strong>don&#8217;t log out</strong> until we test it!):<br />
<code>iptables-restore &lt; /etc/iptables.conf</code></p>
<h3>Testing the rules</h3>
<p>To test the rules, open another terminal and try to access your server:</p>
<p><code>ssh emacs@123.123.123.123</code></p>
<p>If you could access, then the rules should be OK. If not, reload the original rules until you find help:<br />
<code>iptables-restore &lt; /etc/iptables.conf.old</code></p>
<h3>Loading the rules when the server stars</h3>
<p>If the rules we defined work, then our the firewall should be loaded when the server starts:</p>
<p>Create the file /etc/network/if-pre-up.d/iptables with the following contents:<br />
<code>#!/bin/bash<br />
/sbin/iptables-restore &lt; /etc/iptables.conf</code></p>
<p>Then make it executable:<br />
<code>chmod +x /etc/network/if-pre-up.d/iptables</code></p>
<p>We can now go back to our normal user:<br />
<code>exit</code></p>
<h2>Your server is ready!</h2>
<p>At this point, you are ready to start installing applications on your brand-new server!</p>
<h2>What&#8217;s next?</h2>
<ul>
<li>Make sure you already know <a href="http://labor-liber.org/en/gnu-linux/introduction/">the basics of GNU/Linux</a>.</li>
<li><a href="http://wiki.debian.org/">Learn more about Debian</a>.</li>
<li><a href="http://www.debian-administration.org/">Find the HOWTO you were looking for</a>.</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://gustavonarea.net/blog/posts/setting-up-your-first-server/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
	</channel>
</rss>
