本文档旨在描述 Apache™ Subversion® 的 1.7.x 系列。如果您运行的是其他版本的 Subversion,强烈建议您访问 https://svnbook.subversion.org.cn/ 并查阅适合您 Subversion 版本的文档。

基于路径的授权

Apache 和 svnserve 都能够授予(或拒绝)用户权限。通常,这在整个存储库中进行:用户可以读取存储库(或不能),并且可以写入存储库(或不能)。但是,也可以定义更细粒度的访问规则。一组用户可能被允许写入存储库中的某个目录,而其他用户则不能;另一个目录可能只有少数特殊人员才能读取。由于文件也是路径,因此甚至可以限制对每个文件的访问。

这两个服务器都使用通用的文件格式来描述这些基于路径的访问规则。在 Apache 的情况下,需要加载 mod_authz_svn 模块,然后添加 AuthzSVNAccessFile 指令(在 httpd.conf 文件中),指向您自己的访问规则文件。(有关完整说明,请参阅 名为“每个目录的访问控制”的部分。)如果您使用的是 svnserve,则需要使 authz-db 变量(在 svnserve.conf 中)指向您的访问规则文件。

一旦您的服务器知道在哪里找到您的访问文件,就可以开始定义规则了。

该文件的语法与 svnserve.conf 和运行时配置文件使用的熟悉语法相同。以井号 (#) 开头的行将被忽略。在最简单的形式中,每个部分都命名一个存储库及其内部路径,以及经过身份验证的用户名是每个部分内的选项名称。每个选项的值描述了用户对存储库路径的访问级别:r(只读)或 rw(读写)。如果用户根本没有被提及,则不允许访问。

更具体地说:部分名称的值的形式为 [repos-name:path][path]

[Warning] 警告

在 1.7 版本之前,Subversion 在访问控制方面以不区分大小写的方式处理存储库名称和路径,在与访问文件内容进行比较之前,将它们内部转换为小写。现在,它以区分大小写的方式进行这些比较。如果您从旧版本升级到 Subversion 1.7,则应检查您的访问文件以确保大小写正确。

如果您使用的是 SVNParentPath 指令,则在您的部分中指定存储库名称非常重要。如果您省略它们,例如 [/some/dir] 这样的部分将匹配 每个存储库中的 /some/dir 路径。但是,如果您使用的是 SVNPath 指令,则只需在您的部分中定义路径即可——毕竟,只有一个存储库。

[calc:/branches/calc/bug-142]
harry = rw
sally = r

在第一个示例中,用户 harrycalc 仓库中的 /branches/calc/bug-142 目录拥有完全的读写权限,而用户 sally 只有只读权限。其他任何用户都被禁止访问此目录。

[Warning] 警告

mod_dav_svn 提供了一个指令 SVNReposName,允许管理员为仓库定义一个更人性化的名称。

<Location /svn/calc>
  SVNPath /var/svn/calc
  SVNReposName "Calculator Application"
…

这允许 mod_dav_svn 通过除服务器目录基本名称(例如,在前面的示例中为 calc)以外的其他方式来识别仓库,从而在提供仓库内容的目录列表时使用。但是请注意,在查询访问文件以获取授权规则时,Subversion 使用此仓库基本名称进行比较,而不是任何配置的人性化名称。

当然,权限是从父目录继承到子目录的。这意味着我们可以为 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

现在 Sally 可以写入分支的 testing 子目录,但仍然只能读取其他部分。同时,Harry 继续对整个分支拥有完全的读写权限。

还可以通过将用户名变量设置为无来通过继承规则显式拒绝某人的权限。

[calc:/branches/calc/bug-142]
harry = rw
sally = r

[calc:/branches/calc/bug-142/secret]
harry =

在这个例子中,Harry 对整个 bug-142 树拥有读写权限,但对其中的 secret 子目录完全没有访问权限。

[Tip] 提示

要记住的是,最具体的路径总是首先匹配。服务器尝试匹配路径本身,然后匹配路径的父级,然后匹配父级的父级,依此类推。最终效果是,在访问文件中提及特定路径将始终覆盖从父目录继承的任何权限。

默认情况下,任何人都没有访问仓库的权限。这意味着,如果您从一个空文件开始,您可能希望至少为仓库根目录中的所有用户授予读取权限。您可以使用星号变量 (*) 来实现这一点,它表示 所有用户

[/]
* = r

这是一个常见的设置;请注意,节名称中没有提及任何仓库名称。这使得所有仓库对所有用户都是世界可读的。一旦所有用户都获得了对仓库的读访问权限,您就可以在特定仓库内的特定子目录上为特定用户授予显式的 rw 权限。

访问文件还允许您定义整个用户组,就像 Unix 的 /etc/group 文件一样。

[groups]
calc-developers = harry, sally, joe
paint-developers = frank, sally, jane
everyone = harry, sally, joe, frank, jane

组可以像用户一样被授予访问控制。用 at (@) 前缀区分它们。

[calc:/projects/calc]
@calc-developers = rw

[paint:/projects/paint]
jane = r
@paint-developers = rw

另一个重要的事实是,组权限不会被单个用户权限覆盖。相反,会授予所有匹配权限的 组合。在前面的示例中,Jane 是 paint-developers 组的成员,该组具有读写访问权限。结合 jane = r 规则,这仍然使 Jane 具有读写访问权限。组成员的权限只能扩展到组已经拥有的权限之外。无法将属于组的用户限制为低于其组权限的权限。

组也可以被定义为包含其他组。

[groups]
calc-developers = harry, sally, joe
paint-developers = frank, sally, jane
everyone = @calc-developers, @paint-developers

Subversion 1.5 为访问文件语法带来了几个有用的功能——用户名别名、身份验证类令牌和新的规则排除机制——所有这些都进一步简化了访问文件的维护。我们首先描述用户名别名功能。

一些身份验证系统期望并使用我们一直在描述的这种类型的相对较短的用户名——harrysallyjoe 等等。但是其他身份验证系统——例如使用 LDAP 存储或 SSL 客户端证书的系统——可能使用更复杂的用户名。例如,Harry 在 LDAP 保护系统中的用户名可能是 CN=Harold Hacker,OU=Engineers,DC=red-bean,DC=com。对于这样的用户名,访问文件可能会因为长或模糊的用户名而变得非常臃肿,这些用户名很容易拼写错误。幸运的是,用户名别名允许您只需在将更易于理解的别名分配给它的语句中键入一次正确的复杂用户名。

[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
…

定义了一组别名后,您可以在访问文件的其他地方通过别名引用用户,就像您可以在这些地方使用他们的实际用户名一样。只需在别名前面加上一个与号,以将其与普通用户名区分开来。

[groups]
calc-developers = &harry, &sally, &joe
paint-developers = &frank, &sally, &jane
everyone = @calc-developers, @paint-developers

如果您的用户用户名经常更改,您可能也选择使用别名。这样做允许您只需要在用户名更改时更新别名表,而不是对整个访问文件进行全局搜索和替换操作。

Subversion 还支持一些 魔法 令牌,帮助您根据用户的身份验证类别进行规则分配。其中一个令牌是 $authenticated 令牌。在授权规则中,您可以在需要指定用户名、别名或组名的地方使用此令牌,以声明授予任何已使用任何用户名进行身份验证的用户的权限。类似地,使用 $anonymous 令牌,但它匹配所有 使用用户名进行身份验证的用户。

[calendar:/projects/calendar]
$anonymous = r
$authenticated = rw

最后,访问文件语法魔法的另一个有用之处是使用波浪号 (~) 字符作为排除标记。在您的授权规则中,在用户名、别名、组名或身份验证类别令牌前加上波浪号字符,将导致 Subversion 将该规则应用于 匹配该规则的用户。虽然有点不必要的模糊,但以下代码块等同于上一个示例中的代码块

[calendar:/projects/calendar]
~$authenticated = r
~$anonymous = rw

一个不太明显的例子可能是:

[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

以上所有示例都使用目录,因为在目录上定义访问规则是最常见的情况。但同样可以限制对文件路径的访问。

[calendar:/projects/calendar/manager.ics]
harry = rw
sally = r


[61] 本书中的一个常见主题!

[62] 有关更多信息,请参阅博客文章 Authz and Anon Authn Agony,网址为 https://subversion.org.cn/blog/2007-03-27-authz-and-anon-authn-agony.html