9.1第二十章
数据库角色
PostgreSQL使用角色的概念来控制数据库访问权限。角色可以理解为一个数据库用户,也可以是一组用户,取决于角色的创建方式。角色拥有自己的数据库对象(比如,表),角色还可以把访问这些数据库对象的权限授予其他角色。此外,角色还可以把其成员赋给其他角色,以让该成员拥有其他角色的权限。
角色是用户和组的统称。在PostgreSQL 8.1版本前,用户和组是不同的实体,但是现在只有角色的概念。任何角色都可以充当一个用户、一个组或者二者兼而有之。
本章描述如何创建和管理角色。更多信息关于角色权限对数据库对象的影响,请参见第5.6章。
数据库角色
数据库角色和操作系统不是同一个概念。实际使用中使二者统一可能很方便,但这不是必须的。数据库角色在数据库集群中是通用的(并不是每个数据库都不同)。创建角色使用命令 CREATE ROLE SQL :
CREATE ROLE name;
name 遵循SQL命名规范:要么没有特殊字符,要么使用双引号。(实际使用中,通常给该命令加上额外选项,比如 LOGIN。更多细节如下)删除角色使用类似的命令 DROP ROLE :
DROP ROLE name;
为方便起见,程序createuser和dropuser封装了这些SQL命令,可以在shell中直接调用它们:
createuser name dropuser name
通过系统表pg_roles可以查看已存在的角色,例如
SELECT rolname FROM pg_roles;
使用psql程序的元命令\du也可以列出已存在的角色。
为了引导数据库系统,初始化时通常包含一个预定义的角色。这个角色通常是超级用户,默认情况下(除非在运行initdb时改变)它会跟操作系统用户名相同并用此角色来初始化数据库集群。习惯上,该角色被命名为postgres。如果要创建更多的角色必须使用该初始角色连接数据库。
每个数据库服务器的连接必须由某个角色创建,且该角色决定了命令的初始访问权限。为了连接数据库,应用程序客户端需要指定角色来初始化连接请求。例如,psql程序使用-U命令行选项指定角色。很多应用程序假定角色名为当前操作系统用户名(包括createuser和psql)。因此,使角色名和操作系统用户名保持一致是很方便的。
某个连接可以使用的角色集合是由客户断认证设置决定的,这在第19章里有解释。(因此,客户端并非必须使用与操作系统同名的角色名,就像你的登陆用户名不一定是你的真实姓名一样。)因为角色身份决定了一连接客户端可用的权限,所以在多用户环境下仔细配置权限是非常重要的。
角色属性
一个数据库角色可以有一系列属性,这些属性定义它的权限以及与客户认证系统的交互。
- 登陆权限
- 只有具有 LOGIN 属性的角色才可以用作数据库连接的初始角色。拥有 LOGIN 属性的角色几乎等同于“数据库用户”。要创建具有登陆权限的角色,可用下面两个命令之一。
CREATE ROLE name LOGIN; CREATE USER name;
(CREATE USER 区别于 CREATE ROLE 的是 CREATE USER 默认具有登陆属性, 而 CREATE ROLE 没有.)
- 超级用户
- 数据库超级用户除登陆和启动复制外,可以绕过所有权限检查。这是一个危险的权限,应当慎用,最好使用非超级用户来完成你的大部分工作。要创建数据库超级用户,使用 CREATE ROLE name SUPERUSER. 只有超级用户才可以做此操作。创建的超级用户默认授予启动流复制的权限。为了确保安全可以使用CREATE ROLE name SUPERUSER NOREPLICATION 命令禁止该权限.
- 创建数据库
- 角色若要创建数据库必须在创建该角色时明确给出权限(超级用户除外,因为超级用户可以绕开所有权限检查)。要创建这样的角色,使用命令 CREATE ROLE name CREATEDB.
- 创建角色
- 角色若要创建更多的角色必须在创建该角色时明确给出权限(超级用户除外,因为超级用户可以绕开所有权限检查)。要创建这样的角色,使用命令 CREATE ROLE name CREATEROLE. 拥有 CREATEROLE 权限的角色可以修改和删除其他角色,同时还可以授予或撤销它们的成员属性. 但是,若要创建、修改、删除或者变更超级用户角色的成员属性需要有超级用户权限,仅仅 CREATEROLE 是不够的.
- 启动复制
- 角色若要启动流复制必须在创建该角色时明确给出权限。用来启动流复制的角色必须拥有 LOGIN 权限。要创建这样的角色,使用命令 CREATE ROLE name REPLICATION LOGIN.
- 口令
- 口令只有在客户端试图连接数据库且客户短认证方法要求其提供口令时才有用。口令本身和 md5 认证方法可使口令生效。数据库口令和操作系统口令是独立的。创建角色并指定口令使用命令 CREATE ROLE name PASSWORD 'string'.
角色属性可以使用命令 ALTER ROLE 修改。关于 CREATE ROLE 和 ALTER ROLE 命令的更多信息,请查看参考页。
Tip: 推荐在创建一个拥有 CREATEDB 和 CREATEROLE 权限但不是超级用户的角色,并用它完成数据库和角色的日常管理工作。这种方法避免了那些本不需要超级用户权限的误操作。
可以为角色指定运行时缺省配置,这些配置在第18章有描述。例如,处于某种原因,你想让角色连接到数据库时自动关闭索引扫瞄(提示:并不是个好主意),可以使用命名
ALTER ROLE myname SET enable_indexscan TO off;
这样就保存了设置(但不是立即生效)。只是该角色再连接数据库时生效,看起来好像是开启会话前执行了命令 SET enable_indexscan TO off 一样。你也可以在会话中修改该设置,它只是默认行为。若要取消某个默认设置使用命令 ALTER ROLE rolename RESET varname. 给没有 LOGIN 权限的角色设置缺省行为是不会起作用的,因为它们从不会被调用。
角色成员
将用户分组通常可以简化权限管理:这样,权限的授予和撤消可以作用于整个组。PostgreSQL 通过创建一个角色来代表某个组,然后将该组的成员权限授予给其他独立的用户角色。
建立一个组角色,首先要创建角色:
CREATE ROLE name;
通常,作为组角色的角色没有 LOGIN 属性,当然你也可以设置它。
如果组角色存在,可以使用 GRANT 和 REVOKE 命令添加和删除成员:
GRANT group_role TO role1, ... ; REVOKE group_role FROM role1, ... ;
你还可以赋予成员权限给其它组的角色(因为组角色和非组角色没有实质的区别)。数据库不允许建立循环的成员关系。同时,还不允许授予成员权限给 PUBLIC。
组内成员通过两种方式使用组角色的权限。第一,一个组的每个成员都可以明确用 SET ROLE 临时"变成"组角色。在这个状态下,数据库会话具有该组角色的权限, 而不是原始的登录角色权限,这个时候创建的数据库对象被认为是由组角色拥有,而不是登录角色。 第二,拥有 INHERIT 属性的角色成员自动具有它们所属角色组的权限。 例如,假如我们做了下面的事情:
CREATE ROLE joe LOGIN INHERIT; CREATE ROLE admin NOINHERIT; CREATE ROLE wheel NOINHERIT; GRANT admin TO joe; GRANT wheel TO admin;
一旦以角色 joe 连接数据库,该数据库会话便拥有直接赋予 joe 的权限加上任何赋予 admin 的权限,因为 joe "继承"了 admin 的权限。但是,赋予 wheel 的权限不可用,因为即使 joe 是 wheel 的一个间接成员,但该成员关系是通过 admin 传递过来的, 而admin有 NOINHERIT 属性。接着:
SET ROLE admin;
该会话将只拥有那些赋予 admin 的权限,而不包括那些赋予 joe 的权限。 然后:
SET ROLE wheel;
该会话讲只能使用赋予 wheel 的权限,而不包括赋予 joe 或者 admine 的权限。原来的权限可以用下列之一恢复:
SET ROLE joe; SET ROLE NONE; RESET ROLE;
注意: SET ROLE 命令总是允许选取任意登录角色直接或者间接所在的组角色。 因此,在上面的例子里,没必要在变成 wheel 之前先变成 admin。
注意: 在 SQL 标准里,用户和角色之间有一个明确的区别, 用户并不会自动继承权限,而角色可以。这个行为在 PostgreSQL 里面可以通过给予那些当作 SQL 角色使用的角色以 INHERIT 属性, 而给予当作 SQL 用户使用的角色以 NOINHERIT 属性来实现。 不过,PostgreSQL 缺省是给予所有角色 INHERIT 属性, 目的是和 8.1 之前的版本向下兼容,那些版本里,用户总是能使用他们所在组被赋予的权限。
角色属性 LOGIN,SUPERUSER 和 CREATEROLE 可以认为是特殊的权限,但是它们从来不会像数据库对象上的普通权限那样继承。 你必须真实地 SET ROLE 到一个特定的角色,这个角色应该是拥有这些属性的角色, 然后才能利用这些属性。继续上面的例子,我们也可以选择给 admin 角色赋予 CREATEDB 和 CREATEROLE 权限。 但是,已经以 joe 连接的绘话不会立即拥有这些权限,只有在 SET ROLE admin 之后才有。
要删除一个组角色,使用命令 DROP ROLE:
DROP ROLE name;
删除组角色之后,任何在组角色里面的成员关系都会自动撤消(但是成员角色自己则不会受影响)。 不过,请注意任何组角色拥有的对象都必须首先删除或者赋予其它所有者;并且任何赋给该组角色的权限都必须撤消。
函数和触发器
函数和触发器允许用户向后端服务器插入代码,这样其他用户可能无意中执行到这些代码。 因此,两种机制都可以让用户相当容易地给别人设置 "Trojan horse(木马)", 唯一的有效防护就是严格控制谁可以定义函数。
后端服务器里面的函数都是以启动数据库服务器守护进程的操作系统权限执行的。如果编写该函数的编程语言允许无检查的内存访问,服务器内部数据结构都可以被修改。因此,除了其他问题外,这样的函数可以绕过任何系统访问控制。 允许这样访问的函数语言都被认为是"不可信的(untrusted)", PostgreSQL 只允许超级用户使用这样的语言书写函数。