数据库角色
PostgreSQL 使用角色的概念管理数据库访问权限。一个角色可以被看成是一个数据库用户或者是一个数据库用户组,这取决于角色被怎样设置。角色可以拥有数据库对象(例如,表和函数)并且能够把那些对象上的权限赋予给其他角色来控制谁能访问哪些对象。此外,还可以把一个角色中的成员资格授予给另一个角色,这样允许成员角色使用被赋予给另一个角色的权限。
角色的概念把“用户”和“组”的概念都包括在内。任意角色都可以扮演用户、组或者两者。
本章描述如何创建和管理角色。更多角色权限在多个数据库对象上的效果可以在第 5.7 节中找到。
数据库角色在概念上已经完全与操作系统用户独立开来。事实上可能维护一个对应关系会比较方便,但是这并非必需。数据库角色是数据库级别的对象(属于特定的数据库)。要创建一个角色,可使用 SQL 命令 CREATE ROLE:
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
元命令也可以用来列出现有角色。
为了引导数据库系统,一个刚刚被初始化好的系统总是包含一个预定义角色。这个角色总是一个 “superuser”,并且默认情况下(除非在运行initdb
时修改)它的名字和初始化数据库实例的操作系统用户相同。习惯上,这个角色将被命名为postgres
。为了创建更多角色,你首先必须以初始角色的身份连接。
每一个到数据库服务器的连接都是使用某个特定角色名建立的,并且这个角色决定发起连接的命令的初始访问权限。要使用一个特定数据库连接的角色名由客户端指示,该客户端以一种应用相关的风格发起连接请求。例如,psql
程序使用-U
命令行选项来指定要以哪个角色连接。很多应用假定该名字默认是当前操作系统用户(包括createuser
和psql
)。因此在角色和操作系统用户之间维护一个名字对应关系通常是很方便的。
一个给定客户端连接能够用来连接的数据库角色的集合由该客户端的认证设置决定,这些在第 20 章中有解释(因此,一个客户端不止限于以匹配其操作系统用户的角色连接,就像一个人的登录名不需要匹配她的真实名字一样)。因为角色身份决定一个已连接客户端可用的权限集合,在设置一个多用户环境时要小心地配置权限。
一个数据库角色可以有一些属性,它们定义角色的权限并且与客户端认证系统交互。
-
登录权限
只有具有
LOGIN
属性的角色才能被用于一个数据库连接的初始角色名称。一个带有LOGIN
属性的角色可以被认为和一个“数据库用户”相同。要创建一个带有登录权限的角色,使用两者之一:CREATE ROLE
name
LOGIN
;CREATE USER
name
;(CREATE USER
和CREATE ROLE
等效,除了CREATE USER
默认假定有LOGIN
,而CREATE ROLE
不这样认为)。 -
超级用户状态
一个数据库超级用户会绕开所有权限检查,除了登入的权利。这是一个危险的权限并且应该小心使用,最好用一个不是超级用户的角色来完成你的大部分工作。要创建一个新数据库超级用户,使用
CREATE ROLE
name
SUPERUSER
。你必须作为一个超级用户来完成这些。 -
数据库创建
一个角色必须被显式给予权限才能创建数据库(除了超级用户,因为它们会绕开所有权限检查)。要创建这样一个角色,使用
CREATE ROLE
name
CREATEDB
。 -
角色创建
一个角色必须被显式给予权限才能创建更多角色(除了超级用户,因为它们会绕开所有权限检查)。要创建这样一个角色,使用
CREATE ROLE
name
CREATEROLE
。一个带有CREATEROLE
权限的角色也可以修改和删除其他角色,还可以授予或回收角色中的成员关系。然而,要创建、修改、删除或修改一个超级用户角色的成员关系,需要以超级用户的身份操作。CREATEROLE
不足以完成这一切。 -
启动复制
一个角色必须被显式给予权限才能发起流复制(除了超级用户,因为它们会绕开所有权限检查)。一个被用于流复制的角色必须也具有
LOGIN
权限。要创建这样一个角色,使用CREATE ROLE
name
REPLICATION LOGIN
。 -
密码
只有当客户端认证方法要求用户在连接数据库时提供一个口令时,一个口令才有意义。
password
和md5
认证方法使用口令。数据库口令与操作系统命令独立。在角色创建时指定一个口令:CREATE ROLE
name
PASSWORD
’string
’。
在创建后可以用ALTER ROLE
修改一个角色属性。CREATE ROLE 和 ALTER ROLE 命令的细节可见参考页。
一个好习惯是创建一个具有CREATEDB
和CREATEROLE
权限的角色,而不是创建一个超级用户,并且然后用这个角色来完成对数据库和角色的例行管理。这种方法避免了在非必要时作为超级用户操作任务的风险。
对于第 19 章中描述的运行时配置设置,一个角色也可以有角色相关的默认值。例如,如果出于某些原因你希望在每次连接时禁用索引扫描(提示:不是好主意),你可以使用:
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
的一个间接成员,但是该成员关系是通过带NOINHERIT
属性的admin
得到的。在:
SET ROLE admin;
之后,该会话将只拥有授予给admin
的权限,但是没有授予给joe
的权限。在执行:
SET ROLE wheel;
之后,该会话将只拥有授予给wheel
的权限,但是没有授予给joe
或admin
的权限。初始的权限状态可以使用下面命令之一恢复:
SET ROLE joe;
SET ROLE NONE;
RESET ROLE;
SET ROLE
命令总是允许选择原始登录角色的直接或间接组角色。因此,在上面的例子中,在成为wheel
之前不必先成为admin
。
在 SQL 标准中,用户和角色之间的区别很清楚,并且用户不会自动继承权限而角色会继承。这种行为在 PostgreSQL 中也可以实现:为要用作 SQL 角色的角色给予INHERIT
属性,而为要用作 SQL 用户的角色给予NOINHERIT
属性。不过,为了向后兼容 8.1 以前的发布(在其中用户总是拥有它们所在组的权限),PostgreSQL 默认给所有的角色INHERIT
属性。
角色属性LOGIN
、SUPERUSER
、CREATEDB
和CREATEROLE
可以被认为是一种特殊权限,但是它们从来不会像数据库对象上的普通权限那样被继承。要使用这些属性,你必须实际SET ROLE
到一个有这些属性之一的特定角色。继续上述例子,我们可以选择授予CREATEDB
和CREATEROLE
给admin
角色。然后一个以joe
角色连接的会话将不会立即有这些权限,只有在执行了SET ROLE admin
之后才会拥有。
要销毁一个组角色,使用DROP ROLE:
DROP ROLE name;
任何在该组角色中的成员关系会被自动撤销(但是成员角色不会受到影响)。
由于角色可以拥有数据库对象并且能持有访问其他对象的特权,删除一个角色常常并非一次 DROP ROLE 就能解决。任何被该用户所拥有 的对象必须首先被删除或者转移给其他拥有者,并且任何已被授予给该角色的权限必须被收回。
对象的拥有关系可以使用ALTER
命令一次转移出去,例如:
ALTER TABLE bobs_table OWNER TO alice;
此外,REASSIGN OWNED 命令可以被用来把要被删除的角色所拥有的所有对象的拥有关系转移给另一个角色。
一旦任何有价值的对象已经被转移给新的拥有者,任何由被删除角色拥有的剩余对象就可以用 DROP OWNED 命令删除。DROP OWNED
不会删除整个数据库或者表空间,因此如果该角色拥有任何还没有被转移给新拥有者的数据库或者表空间,有必要手工删除它们。
对于那些不属于目标角色的对象,DROP OWNED
也会去移除它们授予给目标角色的任何权限。因为REASSIGN OWNED
不会触碰这类对象,通常有必要运行REASSIGN OWNED
和DROP OWNED
(注意先后顺序!)以完全地移除要被删除角色的对象从属关系。
总之,移除曾经拥有过对象的角色的方法是:
REASSIGN OWNED BY doomed_role TO successor_role;
DROP OWNED BY doomed_role;
DROP ROLE doomed_role;
如果不是所有的拥有对象都被转移给了同一个后继拥有者,最好手工处理异常然后执行上述步骤直到结束。
如果在依赖对象还存在时尝试了DROP ROLE
,它将发出消息标识哪些对象需要被重新授予或者删除。