关于postgresql:连接数据库后切换角色

Switch role after connecting to database

在初始连接后与postgres交互时,是否可以更改用户正在使用的postgresql角色?

数据库将在Web应用程序中使用,我希望对具有连接池的表和模式使用数据库级规则。 从阅读postgresql文档看来,如果我最初作为具有超级用户角色的用户连接,我可以切换角色,但我更愿意最初以具有最小权限的用户连接并根据需要进行切换。 切换时必须指定用户的密码才行(实际上我更喜欢它)。

我错过了什么?

更新:我已经按照@Milen的建议尝试了SET ROLESET SESSION AUTHORIZATION但是如果用户不是超级用户,那么这两个命令似乎都不起作用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$ psql -U test
psql (8.4.4)
TYPE"help" FOR help.

test=> \du test
          List OF roles
 ROLE name | Attributes |   Member OF    
-----------+------------+----------------
 test      |            | {connect_only}

test=> \du test2
          List OF roles
 ROLE name | Attributes |   Member OF    
-----------+------------+----------------
 test2     |            | {connect_only}

test=> SET ROLE test2;
ERROR:  permission denied TO SET ROLE"test2"
test=> \q

1
2
3
4
5
6
7
8
9
10
11
--create a user that you want to use the database as:

CREATE ROLE neil;

--create the user for the web server to connect as:

CREATE ROLE webgui noinherit login password 's3cr3t';

--let webgui set role to neil:

GRANT neil TO webgui; --this looks backwards but is correct.

webgui现在位于neil组中,因此webgui可以调用set role neil。 但是,webgui没有继承neil的权限。

稍后,以webgui身份登录:

1
2
3
4
psql -d some_database -U webgui
(enter s3cr3t AS password)

SET ROLE neil;

webgui不需要superuser权限。

您希望在数据库会话开始时set role并在会话结束时重置它。 在Web应用程序中,这分别对应于从数据库连接池获取连接并释放它。 这是使用Tomcat的连接池和Spring Security的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class SetRoleJdbcInterceptor extends JdbcInterceptor {

    @Override
    public void reset(ConnectionPool connectionPool, PooledConnection pooledConnection) {

        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

        IF(authentication != NULL) {
            try {

                /*
                  use OWASP's ESAPI to encode the username to avoid SQL Injection. Can't use parameters with SET ROLE. Need to write PG codec.

                  Or use a whitelist-map approach
                */

                String username = ESAPI.encoder().encodeForSQL(MY_CODEC, authentication.getName());

                Statement statement = pooledConnection.getConnection().createStatement();
                statement.execute("set role "" + username +""");
                statement.close();
            } catch(SQLException EXP){
                throw NEW RuntimeException(EXP);
            }
        }
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        IF("close".equals(method.getName())){
            Statement statement = ((Connection)proxy).createStatement();
            statement.execute("reset role");
            statement.close();
        }

        RETURN super.invoke(proxy, method, args);
    }
}

看看"SET ROLE"和"SET SESSION AUTHORIZATION"。


如果有人仍然需要它(就像我一样)。

The specified role_name must be a role that the current session user is a member of.
https://www.postgresql.org/docs/10/sql-set-role.html

我们需要使当前会话用户成为角色的成员:

1
2
3
4
CREATE ROLE myrole;
SET ROLE myrole;
GRANT myrole TO myuser;
SET ROLE myrole;

生产:

1
2
3
4
5
6
7
8
9
10
11
12
ROLE ROLE created.


Error starting at line : 4 IN command -
SET ROLE myrole
Error report -
ERROR: permission denied TO SET ROLE"myrole"

GRANT succeeded.


ROLE SET succeeded.