<?xml version="1.0" encoding="utf-8" standalone="no"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>Path-Based Authorization</title> <link rel="stylesheet" type="text/css" href="styles.css" /> <meta name="generator" content="DocBook XSL Stylesheets V1.76.1" /> <link rel="home" href="index.html" title="Version Control with Subversion" /> <link rel="up" href="svn.serverconfig.html" title="Chapter 6. Server Configuration" /> <link rel="prev" href="svn.serverconfig.httpd.html" title="httpd, the Apache HTTP Server" /> <link rel="next" href="svn.serverconfig.operational-logging.html" title="High-level Logging" /> </head> <body> <div class="navheader"> <table width="100%" summary="Navigation header"> <tr> <th colspan="3" align="center">Path-Based Authorization</th> </tr> <tr> <td width="20%" align="left"><a accesskey="p" href="svn.serverconfig.httpd.html">Prev</a> </td> <th width="60%" align="center">Chapter 6. Server Configuration</th> <td width="20%" align="right"> <a accesskey="n" href="svn.serverconfig.operational-logging.html">Next</a></td> </tr> </table> <hr /> </div> <div class="sect1" title="Path-Based Authorization"> <div class="titlepage"> <div> <div> <h2 class="title" style="clear: both"><a id="svn.serverconfig.pathbasedauthz"></a>Path-Based Authorization</h2> </div> </div> </div> <p>Both Apache and <span class="command"><strong>svnserve</strong></span> 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. As files are paths, too, it's even possible to restrict access on a per file basis.</p> <p>Both servers use a common file format to describe these path-based access rules. In the case of Apache, one needs to load the <span class="command"><strong>mod_authz_svn</strong></span> module and then add the <code class="literal">AuthzSVNAccessFile</code> directive (within the <code class="filename">httpd.conf</code> file) pointing to your own access rules file. (For a full explanation, see <a class="xref" href="svn.serverconfig.httpd.html#svn.serverconfig.httpd.authz.perdir" title="Per-directory access control">the section called “Per-directory access control”</a>.) If you're using <span class="command"><strong>svnserve</strong></span>, you need to make the <code class="literal">authz-db</code> variable (within <code class="filename">svnserve.conf</code>) point to your access rules file.</p> <div class="sidebar" title="Do You Really Need Path-Based Access Control?"> <div class="titlepage"> <div> <div> <p class="title"> <strong>Do You Really Need Path-Based Access Control?</strong> </p> </div> </div> </div> <p>A lot of administrators setting up Subversion for the first time tend to jump into path-based access control without giving it a lot of thought. The administrator usually knows which teams of people are working on which projects, so it's easy to jump in and grant certain teams access to certain directories and not others. It seems like a natural thing, and it appeases the administrator's desire to maintain tight control of the repository.</p> <p>Note, though, that there are often invisible (and visible!) costs associated with this feature. In the visible category, the server needs to do a lot more work to ensure that the user has the right to read or write each specific path; in certain situations, there's very noticeable performance loss. In the invisible category, consider the culture you're creating. Most of the time, while certain users <span class="emphasis"><em>shouldn't</em></span> be committing changes to certain parts of the repository, that social contract doesn't need to be technologically enforced. Teams can sometimes spontaneously collaborate with each other; someone may want to help someone else out by committing to an area she doesn't normally work on. By preventing this sort of thing at the server level, you're setting up barriers to unexpected collaboration. You're also creating a bunch of rules that need to be maintained as projects develop, new users are added, and so on. It's a bunch of extra work to maintain.</p> <p>Remember that this is a version control system! Even if somebody accidentally commits a change to something she shouldn't, it's easy to undo the change. And if a user commits to the wrong place with deliberate malice, it's a social problem anyway, and that the problem needs to be dealt with outside Subversion.</p> <p>So, before you begin restricting users' access rights, ask yourself whether there's a real, honest need for this, or whether it's just something that <span class="quote">“<span class="quote">sounds good</span>”</span> to an administrator. Decide whether it's worth sacrificing some server speed, and remember that there's very little risk involved; it's bad to become dependent on technology as a crutch for social problems.<sup>[<a id="idp15246320" href="#ftn.idp15246320" class="footnote">61</a>]</sup></p> <p>As an example to ponder, consider that the Subversion project itself has always had a notion of who is allowed to commit where, but it's always been enforced socially. This is a good model of community trust, especially for open source projects. Of course, sometimes there <span class="emphasis"><em>are</em></span> truly legitimate needs for path-based access control; within corporations, for example, certain types of data really can be sensitive, and access needs to be genuinely restricted to small groups of people.</p> </div> <p>Once your server knows where to find your access file, it's time to define the rules.</p> <p>The syntax of the file is the same familiar one used by <code class="filename">svnserve.conf</code> and the runtime configuration files. Lines that start with a hash (<code class="literal">#</code>) 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 <code class="literal">r</code> (read-only) or <code class="literal">rw</code> (read/write). If the user is not mentioned at all, no access is allowed.</p> <p>To be more specific: the value of the section names is either of the form <code class="literal">[repos-name:path]</code> or of the form <code class="literal">[path]</code>.</p> <div class="warning" title="Warning" style="margin-left: 0.5in; margin-right: 0.5in;"> <table border="0" summary="Warning"> <tr> <td rowspan="2" align="center" valign="top" width="25"> <img alt="[Warning]" src="images/warning.png" /> </td> <th align="left">Warning</th> </tr> <tr> <td align="left" valign="top"> <p>Prior to version 1.7, Subversion treated repository names and paths in a case-insensitive fashion for the purposes of access control, converting them to lower case internally before comparing them against the contents of your access file. It now does these comparisons case-sensitively. If you upgraded to Subversion 1.7 from an older version, you should review your access files for case correctness.</p> </td> </tr> </table> </div> <p>If you're using the <code class="literal">SVNParentPath</code> directive, it's important to specify the repository names in your sections. If you omit them, a section such as <code class="literal">[/some/dir]</code> will match the path <code class="filename">/some/dir</code> in <span class="emphasis"><em>every</em></span> repository. If you're using the <code class="literal">SVNPath</code> directive, however, it's fine to only define paths in your sections—after all, there's only one repository.</p> <div class="informalexample"> <pre class="programlisting"> [calc:/branches/calc/bug-142] harry = rw sally = r </pre> </div> <p>In this first example, the user <code class="literal">harry</code> has full read and write access on the <code class="filename">/branches/calc/bug-142</code> directory in the <code class="literal">calc</code> repository, but the user <code class="literal">sally</code> has read-only access. Any other users are blocked from accessing this directory.</p> <div class="warning" title="Warning" style="margin-left: 0.5in; margin-right: 0.5in;"> <table border="0" summary="Warning"> <tr> <td rowspan="2" align="center" valign="top" width="25"> <img alt="[Warning]" src="images/warning.png" /> </td> <th align="left">Warning</th> </tr> <tr> <td align="left" valign="top"> <p><span class="command"><strong>mod_dav_svn</strong></span> offers a directive, <code class="literal">SVNReposName</code>, which allows administrators to define a more human-friendly name, of sorts, for a repository:</p> <div class="informalexample"> <pre class="programlisting"> <Location /svn/calc> SVNPath /var/svn/calc SVNReposName "Calculator Application" … </pre> </div> <p>This allows <span class="command"><strong>mod_dav_svn</strong></span> to identify the repository by something other than merely its server directory basename—<code class="filename">calc</code>, in the previous example—when providing directory listings of repository content. Be aware, however, that when consulting the access file for authorization rules, Subversion uses this repository basename for comparison, <span class="emphasis"><em>not</em></span> any configured human-friendly name.</p> </td> </tr> </table> </div> <p>Of course, permissions are inherited from parent to child directory. That means we can specify a subdirectory with a different access policy for Sally:</p> <div class="informalexample"> <pre class="programlisting"> [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 </pre> </div> <p>Now Sally can write to the <code class="filename">testing</code> subdirectory of the branch, but can still only read other parts. Harry, meanwhile, continues to have complete read/write access to the whole branch.</p> <p>It's also possible to explicitly deny permission to someone via inheritance rules, by setting the username variable to nothing:</p> <div class="informalexample"> <pre class="programlisting"> [calc:/branches/calc/bug-142] harry = rw sally = r [calc:/branches/calc/bug-142/secret] harry = </pre> </div> <p>In this example, Harry has read/write access to the entire <code class="filename">bug-142</code> tree, but has absolutely no access at all to the <code class="filename">secret</code> subdirectory within it.</p> <div class="tip" title="Tip" style="margin-left: 0.5in; margin-right: 0.5in;"> <table border="0" summary="Tip"> <tr> <td rowspan="2" align="center" valign="top" width="25"> <img alt="[Tip]" src="images/tip.png" /> </td> <th align="left">Tip</th> </tr> <tr> <td align="left" valign="top"> <p>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, 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.</p> </td> </tr> </table> </div> <p>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 (<code class="literal">*</code>), which means <span class="quote">“<span class="quote">all users</span>”</span>:</p> <div class="informalexample"> <pre class="programlisting"> [/] * = r </pre> </div> <p>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 <code class="literal">rw</code> permission to certain users on specific subdirectories within specific repositories.</p> <p>The access file also allows you to define whole groups of users, much like the Unix <code class="filename">/etc/group</code> file:</p> <div class="informalexample"> <pre class="programlisting"> [groups] calc-developers = harry, sally, joe paint-developers = frank, sally, jane everyone = harry, sally, joe, frank, jane </pre> </div> <p>Groups can be granted access control just like users. Distinguish them with an <span class="quote">“<span class="quote">at</span>”</span> (<code class="literal">@</code>) prefix:</p> <div class="informalexample"> <pre class="programlisting"> [calc:/projects/calc] @calc-developers = rw [paint:/projects/paint] jane = r @paint-developers = rw </pre> </div> <p>Another important fact is that group permissions are not overridden by individual user permissions. Rather, the <span class="emphasis"><em>combination</em></span> of all matching permissions is granted. In the prior example, Jane is a member of the <code class="literal">paint-developers</code> group, which has read/write access. Combined with the <code class="literal">jane = r</code> rule, this still gives Jane read/write access. Permissions for group members can only be extended beyond the permissions the group already has. Restricting users who are part of a group to less than their group's permissions is impossible.</p> <p>Groups can also be defined to contain other groups:</p> <div class="informalexample"> <pre class="programlisting"> [groups] calc-developers = harry, sally, joe paint-developers = frank, sally, jane everyone = @calc-developers, @paint-developers </pre> </div> <p>Subversion 1.5 brought several useful features to the access file syntax—username aliases, authentication class tokens, and a new rule exclusion mechanism—all of which further simplify the maintenance of the access file. We'll describe first the username aliases feature.</p> <p>Some authentication systems expect and carry relatively short usernames of the sorts we've been describing here—<code class="literal">harry</code>, <code class="literal">sally</code>, <code class="literal">joe</code>, and so on. But other authentication systems—such as those which 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 <code class="literal">CN=Harold Hacker,OU=Engineers,DC=red-bean,DC=com</code>. 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 digestable alias.</p> <div class="informalexample"> <pre class="programlisting"> [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 … </pre> </div> <p>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:</p> <div class="informalexample"> <pre class="programlisting"> [groups] calc-developers = &harry, &sally, &joe paint-developers = &frank, &sally, &jane everyone = @calc-developers, @paint-developers </pre> </div> <p>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.</p> <p>Subversion also supports some <span class="quote">“<span class="quote">magic</span>”</span> tokens for helping you to make rule assignments based on the user's authentication class. One such token is the <code class="literal">$authenticated</code> token. Use this token where you would otherwise specify a username, alias, or group name in your authorization rules to declare the permissions granted to any user who has authenticated with any username at all. Similarly employed is the <code class="literal">$anonymous</code> token, except that it matches everyone who has <span class="emphasis"><em>not</em></span> authenticated with a username.</p> <div class="informalexample"> <pre class="programlisting"> [calendar:/projects/calendar] $anonymous = r $authenticated = rw </pre> </div> <p>Finally, another handy bit of access file syntax magic is the use of the tilde (<code class="literal">~</code>) character as an exclusion marker. In your authorization rules, prefixing a username, alias, group name, or authentication class token with a tilde character will cause Subversion to apply the rule to users who do <span class="emphasis"><em>not</em></span> match the rule. Though somewhat unnecessarily obfuscated, the following block is equivalent to the one in the previous example:</p> <div class="informalexample"> <pre class="programlisting"> [calendar:/projects/calendar] ~$authenticated = r ~$anonymous = rw </pre> </div> <p>A less obvious example might be as follows:</p> <div class="informalexample"> <pre class="programlisting"> [groups] calc-developers = &harry, &sally, &joe calc-owners = &hewlett, &packard calc = @calc-developers, @calc-owners # Any calc participant has read-write access... [calc:/projects/calc] @calc = rw # ...but only allow the owners to make and modify release tags. [calc:/projects/calc/tags] ~@calc-owners = r </pre> </div> <p>All of the above examples use directories, because defining access rules on them is the most common case. But is similarly able to restrict access on file paths, too.</p> <div class="informalexample"> <pre class="programlisting"> [calendar:/projects/calendar/manager.ics] harry = rw sally = r </pre> </div> <div class="sidebar" title="Partial Readability and Checkouts"> <div class="titlepage"> <div> <div> <p class="title"> <strong>Partial Readability and Checkouts</strong> </p> </div> </div> </div> <p>If you're using Apache as your Subversion server and have made certain subdirectories of your repository unreadable to certain users, you need to be aware of a possible nonoptimal behavior with <span class="command"><strong>svn checkout</strong></span>.</p> <p>When the client requests a checkout or update over HTTP, it makes a single server request and receives a single (often large) server response. When the server receives the request, that is the <span class="emphasis"><em>only</em></span> opportunity Apache has to demand user authentication. This has some odd side effects. For example, if a certain subdirectory of the repository is readable only by user Sally, and user Harry checks out a parent directory, his client will respond to the initial authentication challenge as Harry. As the server generates the large response, there's no way it can resend an authentication challenge when it reaches the special subdirectory; thus the subdirectory is skipped altogether, rather than asking the user to reauthenticate as Sally at the right moment. In a similar way, if the root of the repository is anonymously world-readable, the entire checkout will be done without authentication—again, skipping the unreadable directory, rather than asking for authentication partway through.</p> </div> <div class="footnotes"> <br /> <hr width="100" align="left" /> <div class="footnote"> <p><sup>[<a id="ftn.idp15246320" href="#idp15246320" class="para">61</a>] </sup>A common theme in this book!</p> </div> </div> </div> <div class="navfooter"> <hr /> <table width="100%" summary="Navigation footer"> <tr> <td width="40%" align="left"><a accesskey="p" href="svn.serverconfig.httpd.html">Prev</a> </td> <td width="20%" align="center"> <a accesskey="u" href="svn.serverconfig.html">Up</a> </td> <td width="40%" align="right"> <a accesskey="n" href="svn.serverconfig.operational-logging.html">Next</a></td> </tr> <tr> <td width="40%" align="left" valign="top">httpd, the Apache HTTP Server </td> <td width="20%" align="center"> <a accesskey="h" href="index.html">Home</a> </td> <td width="40%" align="right" valign="top"> High-level Logging</td> </tr> </table> </div> <div xmlns="" id="svn-footer"> <hr /> <p>You are reading <em>Version Control with Subversion</em> (for Subversion 1.7), by Ben Collins-Sussman, Brian W. Fitzpatrick, and C. Michael Pilato.<br /> This work is licensed under the <a href="http://creativecommons.org/licenses/by/2.0/">Creative Commons Attribution License v2.0</a>.<br /> To submit comments, corrections, or other contributions to the text, please visit <a href="http://www.svnbook.com/">http://www.svnbook.com/</a>.</p> </div> </body> </html>