Both Apache and svnserve are capable of granting (or denying) permissions to users. Typically this is done over the entire repository: a user can read the repository (or not), and she can write to the repository (or not). It’s also possible, however, to define finer-grained access rules. One set of users may have permission to write to a certain directory in the repository, but not others; another directory might not even be readable by all but a few special people.
Both servers use a common file format to describe these path-based
access rules. In the case of Apache, one needs to load the mod_authz_svn module and then add the AuthzSVNAccessFile
directive (within the httpd.conf
file) pointing to your own rules file. (For a full explanation, see Per-directory access control.) If you’re using
svnserve, you need to make the authz-db
variable (within svnserve.conf)
point to your rules file.
Once your server knows where to find your rules file, it’s time to define the rules.
The syntax of the file is the same familiar one used by svnserve.conf and the runtime configuration
files. Lines that start with a hash (#
)
are ignored. In its simplest form, each section names a repository and
path within it, as well as the authenticated usernames are the option
names within each section. The value of each option describes the user’s
level of access to the repository path: either r
(read-only) or rw
(read/write). If the user is not mentioned at
all, no access is allowed.
To be more specific: the value of the section names is either of the
form [repos-name:path]
or
of the form [path]
. If you’re using the
SVNParentPath
directive, it’s important
to specify the repository names in your sections. If you omit them, a
section such as [/some/dir]
will match
the path /some/dir in
every repository. If you’re using the SVNPath
directive, however, it’s fine to only
define paths in your sections—after all, there’s only one
repository:
[calc:/branches/calc/bug-142] harry = rw sally = r
In this first example, the user harry
has full read and write access on the
/branches/calc/bug-142 directory in
the calc
repository, but the user
sally
has read-only access. Any other
users are blocked from accessing this directory.
Of course, permissions are inherited from parent to child directory. That means we can specify a subdirectory with a different access policy for Sally:
[calc:/branches/calc/bug-142] harry = rw sally = r # give sally write access only to the 'testing' subdir [calc:/branches/calc/bug-142/testing] sally = rw
Now Sally can write to the testing subdirectory of the branch, but she can still only read other parts. Harry, meanwhile, continues to have complete read/write access to the whole branch.
It’s also possible to explicitly deny permission to someone via inheritance rules, by setting the username variable to nothing:
[calc:/branches/calc/bug-142] harry = rw sally = r [calc:/branches/calc/bug-142/secret] harry =
In this example, Harry has read/write access to the entire bug-142 tree, but he has absolutely no access at all to the secret subdirectory within it.
The thing to remember is that the most specific path always matches first. The server tries to match the path itself, and then the parent of the path, and then the parent of that, and so on. The net effect is that mentioning a specific path in the access file will always override any permissions inherited from parent directories.
By default, nobody has any access to the repository at all. That
means that if you’re starting with an empty file, you’ll probably want to
give at least read permission to all users at the root of the repository.
You can do this by using the asterisk variable (*
), which means “all users”:
[/] * = r
This is a common setup; notice that no repository name is mentioned
in the section name. This makes all repositories world-readable to all
users. Once all users have read access to the repositories, you can give
explicit rw
permission to certain users
on specific subdirectories within specific repositories.
The asterisk variable (*
)
is also worth special mention because it’s the
only pattern that matches an anonymous user. If
you’ve configured your server block to allow a mixture of anonymous and
authenticated access, all users start out accessing anonymously. The
server looks for a *
value defined for
the path being accessed; if it can’t find one, it demands real
authentication from the client.
The access file also allows you to define whole groups of users, much like the Unix /etc/group file:
[groups] calc-developers = harry, sally, joe paint-developers = frank, sally, jane everyone = harry, sally, joe, frank, sally, jane
Groups can be granted access control just like users. Distinguish
them with an “at” (@
)
prefix:
[calc:/projects/calc] @calc-developers = rw [paint:/projects/paint] jane = r @paint-developers = rw
Another important fact is that the first
matching rule is the one that gets applied to a user. In the prior
example, even though Jane is a member of the paint-developers
group (which has read/write
access), the jane = r
rule will be
discovered and matched before the group rule, thus denying Jane write
access.
Groups can also be defined to contain other groups:
[groups] calc-developers = harry, sally, joe paint-developers = frank, sally, jane everyone = @calc-developers, @paint-developers
Subversion 1.5 brings another useful feature to the access file
syntax: username aliases. Some authentication systems expect and carry
relatively short usernames of the sorts we’ve been describing
here—harry
, sally
, joe
,
and so on. But other authentication systems—such as those that use LDAP
stores or SSL client certificates—may carry much more complex usernames.
For example, Harry’s username in an LDAP-protected system might be
CN=Harold
Hacker,OU=Engineers,DC=red-bean,DC=com
. With usernames like
that, the access file can become quite bloated with long or obscure
usernames that are easy to mistype. Fortunately, username aliases allow
you to have to type the correct complex username only once, in a statement
which assigns to it a more easily digestible alias:
[aliases] harry = CN=Harold Hacker,OU=Engineers,DC=red-bean,DC=com sally = CN=Sally Swatterbug,OU=Engineers,DC=red-bean,DC=com joe = CN=Gerald I. Joseph,OU=Engineers,DC=red-bean,DC=com ...
Once you’ve defined a set of aliases, you can refer to the users elsewhere in the access file via their aliases in all the same places you could have instead used their actual usernames. Simply prepend an ampersand to the alias to distinguish it from a regular username:
[groups] calc-developers = &harry, &sally, &joe paint-developers = &frank, &sally, &jane everyone = @calc-developers, @paint-developers
You might also choose to use aliases if your users’ usernames change frequently. Doing so allows you to need to update only the aliases table when these username changes occur, instead of doing global-search-and-replace operations on the whole access file.