Passed
Push — develop ( ce7da4...69770d )
by Felipe
05:27
created

RoleTrait   F

Complexity

Total Complexity 129

Size/Duplication

Total Lines 937
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 129
dl 0
loc 937
rs 1.263
c 0
b 0
f 0

27 Methods

Rating   Name   Duplication   Size   Complexity  
A createGroup() 0 12 3
A changePassword() 0 9 1
F setPrivileges() 0 129 36
A getRole() 0 10 1
A dropGroupMember() 0 8 1
B setRenameUser() 0 25 5
A getMemberOf() 0 13 1
B setRenameRole() 0 58 5
A addGroupMember() 0 8 1
A dropGroup() 0 7 1
A renameRole() 0 8 1
A _encryptPassword() 0 3 1
A getMembers() 0 12 1
A getRoles() 0 13 2
B setUser() 0 21 5
A renameUser() 0 8 1
A dropUser() 0 7 1
F setRole() 0 118 31
A getUsers() 0 7 1
A revokeRole() 0 13 2
C createUser() 0 26 7
A dropRole() 0 7 1
A grantRole() 0 11 2
A getGroup() 0 10 1
A getUser() 0 9 1
A getGroups() 0 5 1
F createRole() 0 58 15

How to fix   Complexity   

Complex Class

Complex classes like RoleTrait often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use RoleTrait, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * PHPPgAdmin v6.0.0-beta.40
5
 */
6
7
namespace PHPPgAdmin\Database;
8
9
/**
10
 * Common trait for roles and users manipulation.
11
 */
12
trait RoleTrait
13
{
14
15
    /**
16
     * Returns all roles in the database cluster.
17
     *
18
     * @param $rolename (optional) The role name to exclude from the select
0 ignored issues
show
Bug introduced by
The type PHPPgAdmin\Database\optional was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
19
     *
20
     * @return All roles
0 ignored issues
show
Bug introduced by
The type PHPPgAdmin\Database\All was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
21
     */
22
    public function getRoles($rolename = '')
23
    {
24
        $sql = '
25
			SELECT rolname, rolsuper, rolcreatedb, rolcreaterole, rolinherit,
26
				rolcanlogin, rolconnlimit, rolvaliduntil, rolconfig
27
			FROM pg_catalog.pg_roles';
28
        if ($rolename) {
29
            $sql .= " WHERE rolname!='{$rolename}'";
30
        }
31
32
        $sql .= ' ORDER BY rolname';
33
34
        return $this->selectSet($sql);
0 ignored issues
show
Bug introduced by
It seems like selectSet() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

34
        return $this->/** @scrutinizer ignore-call */ selectSet($sql);
Loading history...
35
    }
36
37
    /**
38
     * Returns information about a single role.
39
     *
40
     * @param $rolename The name of the role to retrieve
0 ignored issues
show
Bug introduced by
The type PHPPgAdmin\Database\The was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
41
     *
42
     * @return The role's data
43
     */
44
    public function getRole($rolename)
45
    {
46
        $this->clean($rolename);
0 ignored issues
show
Bug introduced by
It seems like clean() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

46
        $this->/** @scrutinizer ignore-call */ 
47
               clean($rolename);
Loading history...
47
48
        $sql = "
49
			SELECT rolname, rolsuper, rolcreatedb, rolcreaterole, rolinherit,
50
				rolcanlogin, rolconnlimit, rolvaliduntil, rolconfig
51
			FROM pg_catalog.pg_roles WHERE rolname='{$rolename}'";
52
53
        return $this->selectSet($sql);
54
    }
55
56
    /**
57
     * Returns all users in the database cluster.
58
     *
59
     * @return All users
60
     */
61
    public function getUsers()
62
    {
63
        $sql = 'SELECT usename, usesuper, usecreatedb, valuntil AS useexpires, useconfig
64
			FROM pg_user
65
			ORDER BY usename';
66
67
        return $this->selectSet($sql);
68
    }
69
70
    /**
71
     * Returns information about a single user.
72
     *
73
     * @param $username The username of the user to retrieve
74
     *
75
     * @return The user's data
76
     */
77
    public function getUser($username)
78
    {
79
        $this->clean($username);
80
81
        $sql = "SELECT usename, usesuper, usecreatedb, valuntil AS useexpires, useconfig
82
			FROM pg_user
83
			WHERE usename='{$username}'";
84
85
        return $this->selectSet($sql);
86
    }
87
88
    /**
89
     * Creates a new role.
90
     *
91
     * @param $rolename     The name of the role to create
92
     * @param $password     A password for the role
93
     * @param $superuser    Boolean whether or not the role is a superuser
94
     * @param $createdb     Boolean whether or not the role can create databases
95
     * @param $createrole   Boolean whether or not the role can create other roles
96
     * @param $inherits     Boolean whether or not the role inherits the privileges from parent roles
97
     * @param $login        Boolean whether or not the role will be allowed to login
98
     * @param $connlimit    Number of concurrent connections the role can make
99
     * @param $expiry       String Format 'YYYY-MM-DD HH:MM:SS'.  '' means never expire
100
     * @param $memberof     (array) Roles to which the new role will be immediately added as a new member
101
     * @param $members      (array) Roles which are automatically added as members of the new role
102
     * @param $adminmembers (array) Roles which are automatically added as admin members of the new role
103
     *
104
     * @return int 0 if operation was successful
105
     */
106
    public function createRole(
107
        $rolename,
108
        $password,
109
        $superuser,
110
        $createdb,
111
        $createrole,
112
        $inherits,
113
        $login,
114
        $connlimit,
115
        $expiry,
116
        $memberof,
117
        $members,
118
        $adminmembers
119
    ) {
120
        $enc = $this->_encryptPassword($rolename, $password);
121
        $this->fieldClean($rolename);
0 ignored issues
show
Bug introduced by
It seems like fieldClean() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

121
        $this->/** @scrutinizer ignore-call */ 
122
               fieldClean($rolename);
Loading history...
122
        $this->clean($enc);
123
        $this->clean($connlimit);
124
        $this->clean($expiry);
125
        $this->fieldArrayClean($memberof);
0 ignored issues
show
Bug introduced by
It seems like fieldArrayClean() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

125
        $this->/** @scrutinizer ignore-call */ 
126
               fieldArrayClean($memberof);
Loading history...
126
        $this->fieldArrayClean($members);
127
        $this->fieldArrayClean($adminmembers);
128
129
        $sql = "CREATE ROLE \"{$rolename}\"";
130
        if ($password != '') {
131
            $sql .= " WITH ENCRYPTED PASSWORD '{$enc}'";
132
        }
133
134
        $sql .= $superuser ? ' SUPERUSER' : ' NOSUPERUSER';
135
        $sql .= $createdb ? ' CREATEDB' : ' NOCREATEDB';
136
        $sql .= $createrole ? ' CREATEROLE' : ' NOCREATEROLE';
137
        $sql .= $inherits ? ' INHERIT' : ' NOINHERIT';
138
        $sql .= $login ? ' LOGIN' : ' NOLOGIN';
139
        if ($connlimit != '') {
140
            $sql .= " CONNECTION LIMIT {$connlimit}";
141
        } else {
142
            $sql .= ' CONNECTION LIMIT -1';
143
        }
144
145
        if ($expiry != '') {
146
            $sql .= " VALID UNTIL '{$expiry}'";
147
        } else {
148
            $sql .= " VALID UNTIL 'infinity'";
149
        }
150
151
        if (is_array($memberof) && sizeof($memberof) > 0) {
152
            $sql .= ' IN ROLE "' . join('", "', $memberof) . '"';
153
        }
154
155
        if (is_array($members) && sizeof($members) > 0) {
156
            $sql .= ' ROLE "' . join('", "', $members) . '"';
157
        }
158
159
        if (is_array($adminmembers) && sizeof($adminmembers) > 0) {
160
            $sql .= ' ADMIN "' . join('", "', $adminmembers) . '"';
161
        }
162
163
        return $this->execute($sql);
0 ignored issues
show
Bug introduced by
It seems like execute() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

163
        return $this->/** @scrutinizer ignore-call */ execute($sql);
Loading history...
164
    }
165
166
    /**
167
     * Helper function that computes encypted PostgreSQL passwords.
168
     *
169
     * @param $username The username
170
     * @param $password The password
171
     *
172
     * @return string
173
     */
174
    public function _encryptPassword($username, $password)
1 ignored issue
show
Coding Style introduced by
Public method name "RoleTrait::_encryptPassword" must not be prefixed with an underscore
Loading history...
175
    {
176
        return 'md5' . md5($password . $username);
177
    }
178
179
    /**
180
     * Adjusts a role's info and renames it.
181
     *
182
     * @param $rolename        The name of the role to adjust
183
     * @param $password        A password for the role
184
     * @param $superuser       Boolean whether or not the role is a superuser
185
     * @param $createdb        Boolean whether or not the role can create databases
186
     * @param $createrole      Boolean whether or not the role can create other roles
187
     * @param $inherits        Boolean whether or not the role inherits the privileges from parent roles
188
     * @param $login           Boolean whether or not the role will be allowed to login
189
     * @param $connlimit       Number of concurrent connections the role can make
190
     * @param $expiry          string Format 'YYYY-MM-DD HH:MM:SS'.  '' means never expire
191
     * @param $memberof        (array) Roles to which the role will be immediately added as a new member
192
     * @param $members         (array) Roles which are automatically added as members of the role
193
     * @param $adminmembers    (array) Roles which are automatically added as admin members of the role
194
     * @param $memberofold     (array) Original roles whose the role belongs to
195
     * @param $membersold      (array) Original roles that are members of the role
196
     * @param $adminmembersold (array) Original roles that are admin members of the role
197
     * @param $newrolename     The new name of the role
198
     *
199
     * @return bool|int 0 success
200
     */
201
    public function setRenameRole(
202
        $rolename,
203
        $password,
204
        $superuser,
205
        $createdb,
206
        $createrole,
207
        $inherits,
208
        $login,
209
        $connlimit,
210
        $expiry,
211
        $memberof,
212
        $members,
213
        $adminmembers,
214
        $memberofold,
215
        $membersold,
216
        $adminmembersold,
217
        $newrolename
218
    ) {
219
        $status = $this->beginTransaction();
0 ignored issues
show
Bug introduced by
It seems like beginTransaction() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

219
        /** @scrutinizer ignore-call */ 
220
        $status = $this->beginTransaction();
Loading history...
220
        if ($status != 0) {
221
            return -1;
222
        }
223
224
        if ($rolename != $newrolename) {
225
            $status = $this->renameRole($rolename, $newrolename);
226
            if ($status != 0) {
227
                $this->rollbackTransaction();
0 ignored issues
show
Bug introduced by
It seems like rollbackTransaction() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

227
                $this->/** @scrutinizer ignore-call */ 
228
                       rollbackTransaction();
Loading history...
228
229
                return -3;
230
            }
231
            $rolename = $newrolename;
232
        }
233
234
        $status =
0 ignored issues
show
Coding Style introduced by
Multi-line assignments must have the equal sign on the second line
Loading history...
235
        $this->setRole(
236
            $rolename,
237
            $password,
238
            $superuser,
239
            $createdb,
240
            $createrole,
241
            $inherits,
242
            $login,
243
            $connlimit,
244
            $expiry,
245
            $memberof,
246
            $members,
247
            $adminmembers,
248
            $memberofold,
249
            $membersold,
250
            $adminmembersold
251
        );
252
        if ($status != 0) {
253
            $this->rollbackTransaction();
254
255
            return -2;
256
        }
257
258
        return $this->endTransaction();
0 ignored issues
show
Bug introduced by
It seems like endTransaction() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

258
        return $this->/** @scrutinizer ignore-call */ endTransaction();
Loading history...
259
    }
260
261
    /**
262
     * Renames a role.
263
     *
264
     * @param $rolename    The name of the role to rename
265
     * @param $newrolename The new name of the role
266
     *
267
     * @return int 0 if operation was successful
268
     */
269
    public function renameRole($rolename, $newrolename)
270
    {
271
        $this->fieldClean($rolename);
272
        $this->fieldClean($newrolename);
273
274
        $sql = "ALTER ROLE \"{$rolename}\" RENAME TO \"{$newrolename}\"";
275
276
        return $this->execute($sql);
277
    }
278
279
    /**
280
     * Adjusts a role's info.
281
     *
282
     * @param $rolename        The name of the role to adjust
283
     * @param $password        A password for the role
284
     * @param $superuser       Boolean whether or not the role is a superuser
285
     * @param $createdb        Boolean whether or not the role can create databases
286
     * @param $createrole      Boolean whether or not the role can create other roles
287
     * @param $inherits        Boolean whether or not the role inherits the privileges from parent roles
288
     * @param $login           Boolean whether or not the role will be allowed to login
289
     * @param $connlimit       Number of concurrent connections the role can make
290
     * @param $expiry          string Format 'YYYY-MM-DD HH:MM:SS'.  '' means never expire
291
     * @param $memberof        (array) Roles to which the role will be immediately added as a new member
292
     * @param $members         (array) Roles which are automatically added as members of the role
293
     * @param $adminmembers    (array) Roles which are automatically added as admin members of the role
294
     * @param $memberofold     (array) Original roles whose the role belongs to
295
     * @param $membersold      (array) Original roles that are members of the role
296
     * @param $adminmembersold (array) Original roles that are admin members of the role
297
     *
298
     * @return int 0 if operation was successful
299
     */
300
    public function setRole(
301
        $rolename,
302
        $password,
303
        $superuser,
304
        $createdb,
305
        $createrole,
306
        $inherits,
307
        $login,
308
        $connlimit,
309
        $expiry,
310
        $memberof,
311
        $members,
312
        $adminmembers,
313
        $memberofold,
314
        $membersold,
315
        $adminmembersold
316
    ) {
317
        $enc = $this->_encryptPassword($rolename, $password);
318
        $this->fieldClean($rolename);
319
        $this->clean($enc);
320
        $this->clean($connlimit);
321
        $this->clean($expiry);
322
        $this->fieldArrayClean($memberof);
323
        $this->fieldArrayClean($members);
324
        $this->fieldArrayClean($adminmembers);
325
326
        $sql = "ALTER ROLE \"{$rolename}\"";
327
        if ($password != '') {
328
            $sql .= " WITH ENCRYPTED PASSWORD '{$enc}'";
329
        }
330
331
        $sql .= $superuser ? ' SUPERUSER' : ' NOSUPERUSER';
332
        $sql .= $createdb ? ' CREATEDB' : ' NOCREATEDB';
333
        $sql .= $createrole ? ' CREATEROLE' : ' NOCREATEROLE';
334
        $sql .= $inherits ? ' INHERIT' : ' NOINHERIT';
335
        $sql .= $login ? ' LOGIN' : ' NOLOGIN';
336
        if ($connlimit != '') {
337
            $sql .= " CONNECTION LIMIT {$connlimit}";
338
        } else {
339
            $sql .= ' CONNECTION LIMIT -1';
340
        }
341
342
        if ($expiry != '') {
343
            $sql .= " VALID UNTIL '{$expiry}'";
344
        } else {
345
            $sql .= " VALID UNTIL 'infinity'";
346
        }
347
348
        $status = $this->execute($sql);
349
350
        if ($status != 0) {
351
            return -1;
352
        }
353
354
        //memberof
355
        $old = explode(',', $memberofold);
356
        foreach ($memberof as $m) {
357
            if (!in_array($m, $old, true)) {
358
                $status = $this->grantRole($m, $rolename);
359
                if ($status != 0) {
360
                    return -1;
361
                }
362
            }
363
        }
364
        if ($memberofold) {
365
            foreach ($old as $o) {
366
                if (!in_array($o, $memberof, true)) {
367
                    $status = $this->revokeRole($o, $rolename, 0, 'CASCADE');
368
                    if ($status != 0) {
369
                        return -1;
370
                    }
371
                }
372
            }
373
        }
374
375
        //members
376
        $old = explode(',', $membersold);
377
        foreach ($members as $m) {
378
            if (!in_array($m, $old, true)) {
379
                $status = $this->grantRole($rolename, $m);
380
                if ($status != 0) {
381
                    return -1;
382
                }
383
            }
384
        }
385
        if ($membersold) {
386
            foreach ($old as $o) {
387
                if (!in_array($o, $members, true)) {
388
                    $status = $this->revokeRole($rolename, $o, 0, 'CASCADE');
389
                    if ($status != 0) {
390
                        return -1;
391
                    }
392
                }
393
            }
394
        }
395
396
        //adminmembers
397
        $old = explode(',', $adminmembersold);
398
        foreach ($adminmembers as $m) {
399
            if (!in_array($m, $old, true)) {
400
                $status = $this->grantRole($rolename, $m, 1);
401
                if ($status != 0) {
402
                    return -1;
403
                }
404
            }
405
        }
406
        if ($adminmembersold) {
407
            foreach ($old as $o) {
408
                if (!in_array($o, $adminmembers, true)) {
409
                    $status = $this->revokeRole($rolename, $o, 1, 'CASCADE');
410
                    if ($status != 0) {
411
                        return -1;
412
                    }
413
                }
414
            }
415
        }
416
417
        return $status;
418
    }
419
420
    /**
421
     * Grants membership in a role.
422
     *
423
     * @param     $role     The name of the target role
424
     * @param     $rolename The name of the role that will belong to the target role
425
     * @param int $admin    (optional) Flag to grant the admin option
426
     *
427
     * @return int 0 if operation was successful
428
     */
429
    public function grantRole($role, $rolename, $admin = 0)
430
    {
431
        $this->fieldClean($role);
432
        $this->fieldClean($rolename);
433
434
        $sql = "GRANT \"{$role}\" TO \"{$rolename}\"";
435
        if ($admin == 1) {
436
            $sql .= ' WITH ADMIN OPTION';
437
        }
438
439
        return $this->execute($sql);
440
    }
441
442
    /**
443
     * Revokes membership in a role.
444
     *
445
     * @param        $role     The name of the target role
446
     * @param        $rolename The name of the role that will not belong to the target role
447
     * @param int    $admin    (optional) Flag to revoke only the admin option
448
     * @param string $type     (optional) Type of revoke: RESTRICT | CASCADE
449
     *
450
     * @return int 0 if operation was successful
451
     */
452
    public function revokeRole($role, $rolename, $admin = 0, $type = 'RESTRICT')
453
    {
454
        $this->fieldClean($role);
455
        $this->fieldClean($rolename);
456
457
        $sql = 'REVOKE ';
458
        if ($admin == 1) {
459
            $sql .= 'ADMIN OPTION FOR ';
460
        }
461
462
        $sql .= "\"{$role}\" FROM \"{$rolename}\" {$type}";
463
464
        return $this->execute($sql);
465
    }
466
467
    /**
468
     * Removes a role.
469
     *
470
     * @param $rolename The name of the role to drop
471
     *
472
     * @return int 0 if operation was successful
473
     */
474
    public function dropRole($rolename)
475
    {
476
        $this->fieldClean($rolename);
477
478
        $sql = "DROP ROLE \"{$rolename}\"";
479
480
        return $this->execute($sql);
481
    }
482
483
    /**
484
     * Creates a new user.
485
     *
486
     * @param $username   The username of the user to create
487
     * @param $password   A password for the user
488
     * @param $createdb   boolean Whether or not the user can create databases
489
     * @param $createuser boolean Whether or not the user can create other users
490
     * @param $expiry     string Format 'YYYY-MM-DD HH:MM:SS'.  '' means never expire
491
     * @param $groups
492
     *
493
     * @return int 0 if operation was successful
494
     *
495
     * @internal param $group (array) The groups to create the user in
496
     */
497
    public function createUser($username, $password, $createdb, $createuser, $expiry, $groups)
498
    {
499
        $enc = $this->_encryptPassword($username, $password);
500
        $this->fieldClean($username);
501
        $this->clean($enc);
502
        $this->clean($expiry);
503
        $this->fieldArrayClean($groups);
504
505
        $sql = "CREATE USER \"{$username}\"";
506
        if ($password != '') {
507
            $sql .= " WITH ENCRYPTED PASSWORD '{$enc}'";
508
        }
509
510
        $sql .= $createdb ? ' CREATEDB' : ' NOCREATEDB';
511
        $sql .= $createuser ? ' CREATEUSER' : ' NOCREATEUSER';
512
        if (is_array($groups) && sizeof($groups) > 0) {
513
            $sql .= ' IN GROUP "' . join('", "', $groups) . '"';
514
        }
515
516
        if ($expiry != '') {
517
            $sql .= " VALID UNTIL '{$expiry}'";
518
        } else {
519
            $sql .= " VALID UNTIL 'infinity'";
520
        }
521
522
        return $this->execute($sql);
523
    }
524
525
    /**
526
     * Adjusts a user's info and renames the user.
527
     *
528
     * @param $username   The username of the user to modify
529
     * @param $password   A new password for the user
530
     * @param $createdb   boolean Whether or not the user can create databases
531
     * @param $createuser boolean Whether or not the user can create other users
532
     * @param $expiry     string Format 'YYYY-MM-DD HH:MM:SS'.  '' means never expire.
533
     * @param $newname    The new name of the user
534
     *
535
     * @return bool|int 0 success
536
     */
537
    public function setRenameUser($username, $password, $createdb, $createuser, $expiry, $newname)
538
    {
539
        $status = $this->beginTransaction();
540
        if ($status != 0) {
541
            return -1;
542
        }
543
544
        if ($username != $newname) {
545
            $status = $this->renameUser($username, $newname);
546
            if ($status != 0) {
547
                $this->rollbackTransaction();
548
549
                return -3;
550
            }
551
            $username = $newname;
552
        }
553
554
        $status = $this->setUser($username, $password, $createdb, $createuser, $expiry);
555
        if ($status != 0) {
556
            $this->rollbackTransaction();
557
558
            return -2;
559
        }
560
561
        return $this->endTransaction();
562
    }
563
564
    /**
565
     * Renames a user.
566
     *
567
     * @param $username The username of the user to rename
568
     * @param $newname  The new name of the user
569
     *
570
     * @return int 0 if operation was successful
571
     */
572
    public function renameUser($username, $newname)
573
    {
574
        $this->fieldClean($username);
575
        $this->fieldClean($newname);
576
577
        $sql = "ALTER USER \"{$username}\" RENAME TO \"{$newname}\"";
578
579
        return $this->execute($sql);
580
    }
581
582
    // Tablespace functions
583
584
    /**
585
     * Adjusts a user's info.
586
     *
587
     * @param $username   The username of the user to modify
588
     * @param $password   A new password for the user
589
     * @param $createdb   boolean Whether or not the user can create databases
590
     * @param $createuser boolean Whether or not the user can create other users
591
     * @param $expiry     string Format 'YYYY-MM-DD HH:MM:SS'.  '' means never expire.
592
     *
593
     * @return int 0 if operation was successful
594
     */
595
    public function setUser($username, $password, $createdb, $createuser, $expiry)
596
    {
597
        $enc = $this->_encryptPassword($username, $password);
598
        $this->fieldClean($username);
599
        $this->clean($enc);
600
        $this->clean($expiry);
601
602
        $sql = "ALTER USER \"{$username}\"";
603
        if ($password != '') {
604
            $sql .= " WITH ENCRYPTED PASSWORD '{$enc}'";
605
        }
606
607
        $sql .= $createdb ? ' CREATEDB' : ' NOCREATEDB';
608
        $sql .= $createuser ? ' CREATEUSER' : ' NOCREATEUSER';
609
        if ($expiry != '') {
610
            $sql .= " VALID UNTIL '{$expiry}'";
611
        } else {
612
            $sql .= " VALID UNTIL 'infinity'";
613
        }
614
615
        return $this->execute($sql);
616
    }
617
618
    /**
619
     * Removes a user.
620
     *
621
     * @param $username The username of the user to drop
622
     *
623
     * @return int 0 if operation was successful
624
     */
625
    public function dropUser($username)
626
    {
627
        $this->fieldClean($username);
628
629
        $sql = "DROP USER \"{$username}\"";
630
631
        return $this->execute($sql);
632
    }
633
634
    /**
635
     * Changes a role's password.
636
     *
637
     * @param $rolename The role name
638
     * @param $password The new password
639
     *
640
     * @return int 0 if operation was successful
641
     */
642
    public function changePassword($rolename, $password)
643
    {
644
        $enc = $this->_encryptPassword($rolename, $password);
645
        $this->fieldClean($rolename);
646
        $this->clean($enc);
647
648
        $sql = "ALTER ROLE \"{$rolename}\" WITH ENCRYPTED PASSWORD '{$enc}'";
649
650
        return $this->execute($sql);
651
    }
652
653
    /**
654
     * Adds a group member.
655
     *
656
     * @param $groname The name of the group
657
     * @param $user    The name of the user to add to the group
658
     *
659
     * @return int 0 if operation was successful
660
     */
661
    public function addGroupMember($groname, $user)
662
    {
663
        $this->fieldClean($groname);
664
        $this->fieldClean($user);
665
666
        $sql = "ALTER GROUP \"{$groname}\" ADD USER \"{$user}\"";
667
668
        return $this->execute($sql);
669
    }
670
671
    /**
672
     * Returns all role names which the role belongs to.
673
     *
674
     * @param $rolename The role name
675
     *
676
     * @return All role names
677
     */
678
    public function getMemberOf($rolename)
679
    {
680
        $this->clean($rolename);
681
682
        $sql = "
683
			SELECT rolname FROM pg_catalog.pg_roles R, pg_auth_members M
684
			WHERE R.oid=M.roleid
685
				AND member IN (
686
					SELECT oid FROM pg_catalog.pg_roles
687
					WHERE rolname='{$rolename}')
688
			ORDER BY rolname";
689
690
        return $this->selectSet($sql);
691
    }
692
693
    // Administration functions
694
695
    /**
696
     * Returns all role names that are members of a role.
697
     *
698
     * @param $rolename The role name
699
     * @param $admin    (optional) Find only admin members
700
     *
701
     * @return All role names
702
     */
703
    public function getMembers($rolename, $admin = 'f')
704
    {
705
        $this->clean($rolename);
706
707
        $sql = "
708
			SELECT rolname FROM pg_catalog.pg_roles R, pg_auth_members M
709
			WHERE R.oid=M.member AND admin_option='{$admin}'
710
				AND roleid IN (SELECT oid FROM pg_catalog.pg_roles
711
					WHERE rolname='{$rolename}')
712
			ORDER BY rolname";
713
714
        return $this->selectSet($sql);
715
    }
716
717
    /**
718
     * Removes a group member.
719
     *
720
     * @param $groname The name of the group
721
     * @param $user    The name of the user to remove from the group
722
     *
723
     * @return int 0 if operation was successful
724
     */
725
    public function dropGroupMember($groname, $user)
726
    {
727
        $this->fieldClean($groname);
728
        $this->fieldClean($user);
729
730
        $sql = "ALTER GROUP \"{$groname}\" DROP USER \"{$user}\"";
731
732
        return $this->execute($sql);
733
    }
734
735
    /**
736
     * Return users in a specific group.
737
     *
738
     * @param $groname The name of the group
739
     *
740
     * @return All users in the group
741
     */
742
    public function getGroup($groname)
743
    {
744
        $this->clean($groname);
745
746
        $sql = "
747
			SELECT s.usename FROM pg_catalog.pg_user s, pg_catalog.pg_group g
748
			WHERE g.groname='{$groname}' AND s.usesysid = ANY (g.grolist)
749
			ORDER BY s.usename";
750
751
        return $this->selectSet($sql);
752
    }
753
754
    /**
755
     * Returns all groups in the database cluser.
756
     *
757
     * @return All groups
758
     */
759
    public function getGroups()
760
    {
761
        $sql = 'SELECT groname FROM pg_group ORDER BY groname';
762
763
        return $this->selectSet($sql);
764
    }
765
766
    /**
767
     * Creates a new group.
768
     *
769
     * @param $groname The name of the group
770
     * @param $users   An array of users to add to the group
0 ignored issues
show
Bug introduced by
The type PHPPgAdmin\Database\An was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
771
     *
772
     * @return int 0 if operation was successful
773
     */
774
    public function createGroup($groname, $users)
775
    {
776
        $this->fieldClean($groname);
777
778
        $sql = "CREATE GROUP \"{$groname}\"";
779
780
        if (is_array($users) && sizeof($users) > 0) {
781
            $this->fieldArrayClean($users);
782
            $sql .= ' WITH USER "' . join('", "', $users) . '"';
783
        }
784
785
        return $this->execute($sql);
786
    }
787
788
    /**
789
     * Removes a group.
790
     *
791
     * @param $groname The name of the group to drop
792
     *
793
     * @return int 0 if operation was successful
794
     */
795
    public function dropGroup($groname)
796
    {
797
        $this->fieldClean($groname);
798
799
        $sql = "DROP GROUP \"{$groname}\"";
800
801
        return $this->execute($sql);
802
    }
803
804
    /**
805
     * Grants a privilege to a user, group or public.
806
     *
807
     * @param $mode        'GRANT' or 'REVOKE';
0 ignored issues
show
Documentation Bug introduced by
The doc comment 'GRANT' at position 0 could not be parsed: Unknown type name ''GRANT'' at position 0 in 'GRANT'.
Loading history...
808
     * @param $type        The type of object
809
     * @param $object      The name of the object
810
     * @param $public      True to grant to public, false otherwise
811
     * @param $usernames   the array of usernames to grant privs to
812
     * @param $groupnames  the array of group names to grant privs to
813
     * @param $privileges  The array of privileges to grant (eg. ('SELECT', 'ALL PRIVILEGES', etc.) )
814
     * @param $grantoption True if has grant option, false otherwise
815
     * @param $cascade     True for cascade revoke, false otherwise
816
     * @param $table       the column's table if type=column
817
     *
818
     * @return int 0 if operation was successful
819
     */
820
    public function setPrivileges(
821
        $mode,
822
        $type,
823
        $object,
824
        $public,
825
        $usernames,
826
        $groupnames,
827
        $privileges,
828
        $grantoption,
829
        $cascade,
830
        $table
831
    ) {
832
        $f_schema = $this->_schema;
833
        $this->fieldClean($f_schema);
834
        $this->fieldArrayClean($usernames);
835
        $this->fieldArrayClean($groupnames);
836
837
        // Input checking
838
        if (!is_array($privileges) || sizeof($privileges) == 0) {
839
            return -3;
840
        }
841
842
        if (!is_array($usernames) || !is_array($groupnames) ||
843
            (!$public && sizeof($usernames) == 0 && sizeof($groupnames) == 0)) {
2 ignored issues
show
Coding Style introduced by
Each line in a multi-line IF statement must begin with a boolean operator
Loading history...
Coding Style introduced by
Closing parenthesis of a multi-line IF statement must be on a new line
Loading history...
844
            return -4;
845
        }
846
847
        if ($mode != 'GRANT' && $mode != 'REVOKE') {
848
            return -5;
849
        }
850
851
        $sql = $mode;
852
853
        // Grant option
854
        if ($this->hasGrantOption() && $mode == 'REVOKE' && $grantoption) {
0 ignored issues
show
Bug introduced by
It seems like hasGrantOption() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

854
        if ($this->/** @scrutinizer ignore-call */ hasGrantOption() && $mode == 'REVOKE' && $grantoption) {
Loading history...
855
            $sql .= ' GRANT OPTION FOR';
856
        }
857
858
        if (in_array('ALL PRIVILEGES', $privileges, true)) {
859
            $sql .= ' ALL PRIVILEGES';
860
        } else {
861
            if ($type == 'column') {
862
                $this->fieldClean($object);
863
                $sql .= ' ' . join(" (\"{$object}\"), ", $privileges);
864
            } else {
865
                $sql .= ' ' . join(', ', $privileges);
866
            }
867
        }
868
869
        switch ($type) {
870
            case 'column':
871
                $sql .= " (\"{$object}\")";
872
                $object = $table;
873
            // no break
874
            case 'table':
875
            case 'view':
876
            case 'sequence':
877
                $this->fieldClean($object);
878
                $sql .= " ON \"{$f_schema}\".\"{$object}\"";
879
880
                break;
881
            case 'database':
882
                $this->fieldClean($object);
883
                $sql .= " ON DATABASE \"{$object}\"";
884
885
                break;
886
            case 'function':
887
                // Function comes in with $object as function OID
888
                $fn = $this->getFunction($object);
0 ignored issues
show
Bug introduced by
It seems like getFunction() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

888
                /** @scrutinizer ignore-call */ 
889
                $fn = $this->getFunction($object);
Loading history...
889
                $this->fieldClean($fn->fields['proname']);
890
                $sql .= " ON FUNCTION \"{$f_schema}\".\"{$fn->fields['proname']}\"({$fn->fields['proarguments']})";
891
892
                break;
893
            case 'language':
894
                $this->fieldClean($object);
895
                $sql .= " ON LANGUAGE \"{$object}\"";
896
897
                break;
898
            case 'schema':
899
                $this->fieldClean($object);
900
                $sql .= " ON SCHEMA \"{$object}\"";
901
902
                break;
903
            case 'tablespace':
904
                $this->fieldClean($object);
905
                $sql .= " ON TABLESPACE \"{$object}\"";
906
907
                break;
908
            default:
909
                return -1;
910
        }
911
912
        // Dump PUBLIC
913
        $first = true;
914
        $sql .= ($mode == 'GRANT') ? ' TO ' : ' FROM ';
915
        if ($public) {
916
            $sql .= 'PUBLIC';
917
            $first = false;
918
        }
919
        // Dump users
920
        foreach ($usernames as $v) {
921
            if ($first) {
922
                $sql .= "\"{$v}\"";
923
                $first = false;
924
            } else {
925
                $sql .= ", \"{$v}\"";
926
            }
927
        }
928
        // Dump groups
929
        foreach ($groupnames as $v) {
930
            if ($first) {
931
                $sql .= "GROUP \"{$v}\"";
932
                $first = false;
933
            } else {
934
                $sql .= ", GROUP \"{$v}\"";
935
            }
936
        }
937
938
        // Grant option
939
        if ($this->hasGrantOption() && $mode == 'GRANT' && $grantoption) {
940
            $sql .= ' WITH GRANT OPTION';
941
        }
942
943
        // Cascade revoke
944
        if ($this->hasGrantOption() && $mode == 'REVOKE' && $cascade) {
945
            $sql .= ' CASCADE';
946
        }
947
948
        return $this->execute($sql);
949
    }
950
951
}
952