本文档旨在描述 Subversion 1.6.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] 警告

Subversion 在访问控制方面以不区分大小写的方式处理仓库名称和路径,在将它们与访问文件的内容进行比较之前,先将它们内部转换为小写。在您的访问文件中的部分标题内容中使用小写。

如果您使用的是 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 继续对整个分支具有完全的读/写访问权限。

也可以通过继承规则显式地拒绝某人的权限,方法是将用户名变量设置为 null。

[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 客户端证书的系统)可能携带更复杂的用户名。例如,在受 LDAP 保护的系统中,Harry 的用户名可能是 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


[53] 本书中的一个共同主题!