Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 35
0.00% covered (danger)
0.00%
0 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
PostgresUtils
0.00% covered (danger)
0.00%
0 / 35
0.00% covered (danger)
0.00%
0 / 6
306
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 canCreateAccounts
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
12
 isSuperUser
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
 canCreateObjectsForWebUser
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
12
 getInstallUserPermissions
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
6
 isRoleMember
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
42
1<?php
2
3namespace MediaWiki\Installer\Task;
4
5use MediaWiki\MainConfigNames;
6use Wikimedia\Rdbms\IMaintainableDatabase;
7
8/**
9 * @internal For use by the installer
10 */
11class PostgresUtils {
12    private const MAX_ROLE_SEARCH_DEPTH = 5;
13
14    /** @var ITaskContext */
15    private $context;
16
17    public function __construct( ITaskContext $context ) {
18        $this->context = $context;
19    }
20
21    public function canCreateAccounts() {
22        $perms = $this->getInstallUserPermissions();
23        return ( $perms && $perms->rolsuper ) || $perms->rolcreaterole;
24    }
25
26    public function isSuperUser() {
27        $perms = $this->getInstallUserPermissions();
28        return $perms && $perms->rolsuper;
29    }
30
31    /**
32     * Returns true if the install user is able to create objects owned
33     * by the web user, false otherwise.
34     * @return bool
35     */
36    public function canCreateObjectsForWebUser() {
37        if ( $this->isSuperUser() ) {
38            return true;
39        }
40
41        $status = $this->context->getConnection( ITaskContext::CONN_CREATE_DATABASE );
42        if ( !$status->isOK() ) {
43            return false;
44        }
45        $conn = $status->getDB();
46        $installerId = $conn->selectField( '"pg_catalog"."pg_roles"', 'oid',
47            [ 'rolname' => $this->context->getOption( 'InstallUser' ) ], __METHOD__ );
48        $webId = $conn->selectField( '"pg_catalog"."pg_roles"', 'oid',
49            [ 'rolname' => $this->context->getConfigVar( MainConfigNames::DBuser ) ], __METHOD__ );
50
51        return self::isRoleMember( $conn, $installerId, $webId, self::MAX_ROLE_SEARCH_DEPTH );
52    }
53
54    private function getInstallUserPermissions() {
55        $status = $this->context->getConnection( ITaskContext::CONN_CREATE_DATABASE );
56        if ( !$status->isOK() ) {
57            return false;
58        }
59        $conn = $status->getDB();
60        $superuser = $this->context->getOption( 'InstallUser' );
61
62        $row = $conn->selectRow( '"pg_catalog"."pg_roles"', '*',
63            [ 'rolname' => $superuser ], __METHOD__ );
64
65        return $row;
66    }
67
68    /**
69     * Recursive helper for canCreateObjectsForWebUser().
70     * @param IMaintainableDatabase $conn
71     * @param int $targetMember Role ID of the member to look for
72     * @param int $group Role ID of the group to look for
73     * @param int $maxDepth Maximum recursive search depth
74     * @return bool
75     */
76    private function isRoleMember( $conn, $targetMember, $group, $maxDepth ) {
77        if ( $targetMember === $group ) {
78            // A role is always a member of itself
79            return true;
80        }
81        // Get all members of the given group
82        $res = $conn->select( '"pg_catalog"."pg_auth_members"', [ 'member' ],
83            [ 'roleid' => $group ], __METHOD__ );
84        foreach ( $res as $row ) {
85            if ( $row->member == $targetMember ) {
86                // Found target member
87                return true;
88            }
89            // Recursively search each member of the group to see if the target
90            // is a member of it, up to the given maximum depth.
91            if ( $maxDepth > 0 &&
92                $this->isRoleMember( $conn, $targetMember, $row->member, $maxDepth - 1 )
93            ) {
94                // Found member of member
95                return true;
96            }
97        }
98
99        return false;
100    }
101}