The repoze.what Hgwebdir plugin

Author:Yaco Sistemas (Lorenzo Gil)
Latest release:0.1.1

Overview

This plugin allows to override the hgwebdir authorization support with a more sophisticated one that allows more flexibility and maintainability.

It allows to define your repository access information in a single file and has some utility functions to protect a wsgi application like Mercurial include hgwebdir.

How to install

The minimum requirements are repze.what and you can install it all with easy_install:

easy_install repoze.what.plugins.hgwebdir

The development mainline is available at the following Mercurial repository:

hg clone http://bitbucket.org/lgs/repoze.what.plugins.hgwebdir

How to get help

The prefered place to ask questions is the #yaco.es #yaco.es IRC channel. Bugs reports and feature requests should be sent to the issue tracker of Bitbucket.

How to use it

This plugin includes a repoze.what Group source, a Permission source, a couple of predicate checkers and an utility middleware to protect the hgwebdir application.

Group and permission sources

The group and permission sources are based on Subversion mod_authz_svn and they both use a single file very similar to the one Subversion uses. This file is a ini like configuration file where you have sections and inside them, you have options and values.

The first section is a special one called groups. In this section you define the groups of users that you have. To define a group put the name of the group as the option name and the user names of the members of such groups as the value. The members is a list of user ids separated by spaces and (optionally) commas.

Let’s see an example:

[groups]
rolling = mick, keith, ronnie, charlie
beatles = john, paul, george, ringo

Here you have two groups, each one having four members.

After the groups section you will have a section for each Mercurial repository you want to serve. If you have a repository and it is not listed on this file the default behaviour is to forbid any access to it.

So you have a section for each repository. Inside the section you define who can access to it. There are basically two operations on a repository, reading it or writing to it. So each option inside a repository will define who can read or write it. A option starting with @ references the name of a group (from the groups section). A option without the @ means just a user id. An * means everybody. The value can be r, w or rw if you want to give read, write or read and write access respectively.

Let’s see another example:

[sticky-fingers]
@rolling = rw
@beatles = r

[sargent-peppers]
@beatles = rw
@rolling = r

[pet-sounds]
* = r

Here there are three repositories: sticky-fingers, sargent-peppers and pet-sounds. In the sticky-fingers repository the members of the rolling group have read and write permissions while the members of the beatles group have only read permissions. In the sargent-peppers repository is the other way around and lastly, in the pet-sounds repository, everybody have read access and nobody can write.

Now you are ready to use the HgwebdirGroupsAdapter and the HgwebdirPermissionsAdapter. The only thing both of these classes need is the path to your authz file. Theorically you can instantiate each of these classes with a different auth file but in practice that doesn’t make much sense.

Let’s see a full example of how o integrate these sources with the rest of repoze.what and repoze.who funcionality:

from repoze.who.plugins.basicauth import BasicAuthPlugin
from repoze.who.plugins.htpasswd import HTPasswdPlugin, crypt_check
from repoze.what.middleware import setup_auth

from repoze.what.plugins.hgwebdir.adapters import HgwebdirGroupsAdapter
from repoze.what.plugins.hgwebdir.adapters import HgwebdirPermissionsAdapter


def add_auth(app, users_file, authz_file):
    """
    Add authentication and authorization middleware to the ``app``.

    :param app: The WSGI application.
    :param users_file: a httpasswd file with users and passwords
    :param authz_file: an ini file with group and permissions
    :return: The same WSGI application, with authentication and
        authorization middleware.

    """
    groups = {'all_groups': HgwebdirGroupsAdapter(authz_file)}

    permissions = {'all_perms': HgwebdirPermissionsAdapter(authz_file)}

    basicauth = BasicAuthPlugin('Your realm')
    identifiers = [('basicauth', basicauth)]

    htpasswd_auth = HTPasswdPlugin(users_file, crypt_check)
    authenticators = [('htpasswd_auth', htpasswd_auth)]

    challengers = [('basicauth', basicauth)]

    app_with_auth = setup_auth(
        app,
        groups,
        permissions,
        identifiers=identifiers,
        authenticators=authenticators,
        challengers=challengers)
    return app_with_auth

In the repoze.what.plugins.hgwebdir.adapters module ther is another function called get_public_repositories. It needs the path to the authz file and will return a list of repository names that are public. A repository is public if it has at least an option like * = r. In the above example this function would return a list with one element, the pet-sounds repository:

>>> from repoze.what.plugins.hgwebdir.adapters import get_public_repositories

>>> get_public_repositories('/path/to/authz.ini')
[u'pet-sounds']

Predicate checkers

This plugin has two new predicate checkers: is_read_access and has_repo_access.

The is_read_access predicate checker is quite simple since it does not need any parameter in its constructor and it just check whetever the request is using a GET http verb or not:

from repoze.what.plugins.hgwebdir.predicates import is_read_access

p = is_read_access()

The other predicate checker, has_repo_access is quite more interesting. It is a compuound predicate checker that test several things. It needs the name of the repository it is trying to protect and It will allow access to such resource if any of the following assertions is true:

  • The request is a read action and the user has the permission made from the name of the repository plus the string -read. For example, if the repository is called project23 the user will need the permission project23-read.
  • The request is not a read action and the user has the permission made from the name of the repository plus the string -write. For example, if the repository is called project23 the user will need the permission project23-write.
  • The request is a read action and the repository is public.

This predicate checker has two construction parameters:

  • repo_name: the name of the repository
  • public_repos: a list of repository names that are public

Let’s see an example of its use:

from repoze.what.plugins.hgwebdir.predicates import has_repo_access

p = has_repo_access('project12', ['public-project1', 'public-project2'])

Middleware

In the middleware land of this plugin you will find a single function, the protect_repositories callable. Give it the wsgi application that is returned by the mercurial.hgweb.hgwebdir_mod.hgwebdir function and it will add authorization support by using the has_repo_access predicate checker that is explained above.

The second argument to the protect_repositories function is a list of public repositories. These repositories should be granted read access to everybody.

As you can see this function ties everything together into a useful piece of middleware. Let’s see an example of its use:

from mercurial.hgweb.hgwebdir_mod import hgwebdir

from repoze.what.plugins.hgwebdir.adapters import get_public_repositories
from repoze.what.plugins.hgwebdir.middleware import protect_repositories

def make_app(hg_config, users_file, authz_file):
    original_app = hgwebdir(hg_config)
    secured_app = protect_repositories(original_app,
                                       get_public_repositories(authz_file))
    return add_auth(secured_app, users_file, authz_file)

The add_auth function was explained in the Group and Permissions sources section.

Contents:

Indices and tables