Passed
Push — develop ( 49abac...9864d8 )
by Felipe
05:09
created

RoleTrait::_dealWithOldParentRoles()   C

Complexity

Conditions 7
Paths 13

Size

Total Lines 25
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 25
rs 6.7272
c 0
b 0
f 0
cc 7
eloc 12
nc 13
nop 3
1
<?php
2
3
/**
4
 * PHPPgAdmin v6.0.0-beta.43
5
 */
6
7
namespace PHPPgAdmin\Traits;
8
9
/**
10
 * Common trait for roles and users manipulation.
11
 */
12
trait RoleTrait
13
{
14
    /**
15
     * Returns all roles in the database cluster.
16
     *
17
     * @param string $rolename (optional) The role name to exclude from the select
18
     *
19
     * @return \PHPPgAdmin\ADORecordSet Either one or All roles
20
     */
21
    public function getRoles($rolename = '')
22
    {
23
        $sql = '
24
			SELECT rolname, rolsuper, rolcreatedb, rolcreaterole, rolinherit,
25
				rolcanlogin, rolconnlimit, rolvaliduntil, rolconfig
26
			FROM pg_catalog.pg_roles';
27
        if ($rolename) {
28
            $sql .= " WHERE rolname!='{$rolename}'";
29
        }
30
31
        $sql .= ' ORDER BY rolname';
32
33
        return $this->selectSet($sql);
34
    }
35
36
    /**
37
     * Returns information about a single role.
38
     *
39
     * @param string $rolename The name of the role to retrieve
40
     *
41
     * @return \PHPPgAdmin\ADORecordSet The role's data
42
     */
43
    public function getRole($rolename)
44
    {
45
        $this->clean($rolename);
46
47
        $sql = "
48
			SELECT rolname, rolsuper, rolcreatedb, rolcreaterole, rolinherit,
49
				rolcanlogin, rolconnlimit, rolvaliduntil, rolconfig
50
			FROM pg_catalog.pg_roles WHERE rolname='{$rolename}'";
51
52
        return $this->selectSet($sql);
53
    }
54
55
    /**
56
     * Returns all users in the database cluster.
57
     *
58
     * @return \PHPPgAdmin\ADORecordSet All users
59
     */
60
    public function getUsers()
61
    {
62
        $sql = 'SELECT usename, usesuper, usecreatedb, valuntil AS useexpires, useconfig
63
			FROM pg_user
64
			ORDER BY usename';
65
66
        return $this->selectSet($sql);
67
    }
68
69
    /**
70
     * Returns information about a single user.
71
     *
72
     * @param string $username The username of the user to retrieve
73
     *
74
     * @return \PHPPgAdmin\ADORecordSet The user's data
75
     */
76
    public function getUser($username)
77
    {
78
        $this->clean($username);
79
80
        $sql = "SELECT usename, usesuper, usecreatedb, valuntil AS useexpires, useconfig
81
			FROM pg_user
82
			WHERE usename='{$username}'";
83
84
        return $this->selectSet($sql);
85
    }
86
87
    /**
88
     * Creates a new role.
89
     *
90
     * @param string $rolename     The name of the role to create
91
     * @param string $password     A password for the role
92
     * @param bool   $superuser    Boolean whether or not the role is a superuser
93
     * @param bool   $createdb     Boolean whether or not the role can create databases
94
     * @param bool   $createrole   Boolean whether or not the role can create other roles
95
     * @param bool   $inherits     Boolean whether or not the role inherits the privileges from parent roles
96
     * @param bool   $login        Boolean whether or not the role will be allowed to login
97
     * @param number $connlimit    Number of concurrent connections the role can make
98
     * @param string $expiry       String Format 'YYYY-MM-DD HH:MM:SS'.  '' means never expire
99
     * @param array  $new_roles_to_add     (array) Roles to which the new role will be immediately added as a new member
100
     * @param array  $new_members_of_role      (array) Roles which are automatically added as members of the new role
101
     * @param array  $new_admins_of_role (array) Roles which are automatically added as admin members of the new role
102
     *
103
     * @return int 0 if operation was successful
104
     */
105
    public function createRole(
106
        $rolename,
107
        $password,
108
        $superuser,
109
        $createdb,
110
        $createrole,
111
        $inherits,
112
        $login,
113
        $connlimit,
114
        $expiry,
115
        $new_roles_to_add,
116
        $new_members_of_role,
117
        $new_admins_of_role
118
    ) {
119
        $enc = $this->_encryptPassword($rolename, $password);
120
        $this->fieldClean($rolename);
121
        $this->clean($enc);
122
        $this->clean($connlimit);
123
        $this->clean($expiry);
124
        $this->fieldArrayClean($new_roles_to_add);
125
        $this->fieldArrayClean($new_members_of_role);
126
        $this->fieldArrayClean($new_admins_of_role);
127
128
        $sql = "CREATE ROLE \"{$rolename}\"";
129
        if ($password != '') {
130
            $sql .= " WITH ENCRYPTED PASSWORD '{$enc}'";
131
        }
132
133
        $sql .= $superuser ? ' SUPERUSER' : ' NOSUPERUSER';
134
        $sql .= $createdb ? ' CREATEDB' : ' NOCREATEDB';
135
        $sql .= $createrole ? ' CREATEROLE' : ' NOCREATEROLE';
136
        $sql .= $inherits ? ' INHERIT' : ' NOINHERIT';
137
        $sql .= $login ? ' LOGIN' : ' NOLOGIN';
138
        if ($connlimit != '') {
139
            $sql .= " CONNECTION LIMIT {$connlimit}";
140
        } else {
141
            $sql .= ' CONNECTION LIMIT -1';
142
        }
143
144
        if ($expiry != '') {
145
            $sql .= " VALID UNTIL '{$expiry}'";
146
        } else {
147
            $sql .= " VALID UNTIL 'infinity'";
148
        }
149
150
        if (is_array($new_roles_to_add) && sizeof($new_roles_to_add) > 0) {
151
            $sql .= ' IN ROLE "' . join('", "', $new_roles_to_add) . '"';
152
        }
153
154
        if (is_array($new_members_of_role) && sizeof($new_members_of_role) > 0) {
155
            $sql .= ' ROLE "' . join('", "', $new_members_of_role) . '"';
156
        }
157
158
        if (is_array($new_admins_of_role) && sizeof($new_admins_of_role) > 0) {
159
            $sql .= ' ADMIN "' . join('", "', $new_admins_of_role) . '"';
160
        }
161
162
        return $this->execute($sql);
163
    }
164
165
    /**
166
     * Helper function that computes encypted PostgreSQL passwords.
167
     *
168
     * @param string $username The username
169
     * @param string $password The password
170
     *
171
     * @return string
172
     */
173
    public function _encryptPassword($username, $password)
174
    {
175
        return 'md5' . md5($password . $username);
176
    }
177
178
    /**
179
     * Adjusts a role's info and renames it.
180
     *
181
     * @param string $rolename        The name of the role to adjust
182
     * @param string $password        A password for the role
183
     * @param bool   $superuser       Boolean whether or not the role is a superuser
184
     * @param bool   $createdb        Boolean whether or not the role can create databases
185
     * @param bool   $createrole      Boolean whether or not the role can create other roles
186
     * @param bool   $inherits        Boolean whether or not the role inherits the privileges from parent roles
187
     * @param bool   $login           Boolean whether or not the role will be allowed to login
188
     * @param number $connlimit       Number of concurrent connections the role can make
189
     * @param string $expiry          string Format 'YYYY-MM-DD HH:MM:SS'.  '' means never expire
190
     *
191
     * @param array  $new_roles_to_add        (array) Roles to which the role will be immediately added as a new member
0 ignored issues
show
Coding Style introduced by
Parameter tags must be grouped together in a doc comment
Loading history...
192
     * @param array  $new_members_of_role         (array) Roles which are automatically added as members of the role
0 ignored issues
show
Coding Style introduced by
Parameter tags must be grouped together in a doc comment
Loading history...
193
     * @param array  $new_admins_of_role    (array) Roles which are automatically added as admin members of the role
0 ignored issues
show
Coding Style introduced by
Parameter tags must be grouped together in a doc comment
Loading history...
194
     *
195
     * @param string  $original_parent_roles     Original roles whose the role belongs to, comma separated
0 ignored issues
show
Coding Style introduced by
Parameter tags must be grouped together in a doc comment
Loading history...
196
     * @param string  $original_members      Original roles that are members of the role, comma separated
0 ignored issues
show
Coding Style introduced by
Parameter tags must be grouped together in a doc comment
Loading history...
197
     * @param string  $original_admins Original roles that are admin members of the role, comma separated
0 ignored issues
show
Coding Style introduced by
Parameter tags must be grouped together in a doc comment
Loading history...
198
     * @param string $newrolename     The new name of the role
0 ignored issues
show
Coding Style introduced by
Parameter tags must be grouped together in a doc comment
Loading history...
199
     *
200
     * @return bool|int 0 success
201
     */
202
    public function setRenameRole(
203
        $rolename,
204
        $password,
205
        $superuser,
206
        $createdb,
207
        $createrole,
208
        $inherits,
209
        $login,
210
        $connlimit,
211
        $expiry,
212
        $new_roles_to_add,
213
        $new_members_of_role,
214
        $new_admins_of_role,
215
        $original_parent_roles,
216
        $original_members,
217
        $original_admins,
218
        $newrolename
219
    ) {
220
        $status = $this->beginTransaction();
221
        if ($status != 0) {
222
            return -1;
223
        }
224
225
        if ($rolename != $newrolename) {
226
            $status = $this->renameRole($rolename, $newrolename);
227
            if ($status != 0) {
228
                $this->rollbackTransaction();
229
230
                return -3;
231
            }
232
            $rolename = $newrolename;
233
        }
234
235
        $status =
0 ignored issues
show
Coding Style introduced by
Multi-line assignments must have the equal sign on the second line
Loading history...
236
        $this->setRole(
237
            $rolename,
238
            $password,
239
            $superuser,
240
            $createdb,
241
            $createrole,
242
            $inherits,
243
            $login,
244
            $connlimit,
245
            $expiry,
246
            $new_roles_to_add,
247
            $new_members_of_role,
248
            $new_admins_of_role,
249
            $original_parent_roles,
250
            $original_members,
251
            $original_admins
252
        );
253
        if ($status != 0) {
254
            $this->rollbackTransaction();
255
256
            return -2;
257
        }
258
259
        return $this->endTransaction();
260
    }
261
262
    /**
263
     * Renames a role.
264
     *
265
     * @param string $rolename    The name of the role to rename
266
     * @param string $newrolename The new name of the role
267
     *
268
     * @return int 0 if operation was successful
269
     */
270
    public function renameRole($rolename, $newrolename)
271
    {
272
        $this->fieldClean($rolename);
273
        $this->fieldClean($newrolename);
274
275
        $sql = "ALTER ROLE \"{$rolename}\" RENAME TO \"{$newrolename}\"";
276
277
        return $this->execute($sql);
278
    }
279
280
    private function _dealWithOldParentRoles($original_parent_roles, $new_roles_to_add, $rolename)
281
    {
282
        $old = explode(',', $original_parent_roles);
283
284
        // Grant the roles of the old role owners to the new owner
285
        foreach ($new_roles_to_add as $m) {
286
            if (!in_array($m, $old, true)) {
287
                $status = $this->grantRole($m, $rolename);
288
                if ($status != 0) {
289
                    return -1;
290
                }
291
            }
292
        }
293
294
        // Revoke the new role to the old members if they don't have the requested role name
295
296
        foreach ($old as $o) {
297
            if (!in_array($o, $new_roles_to_add, true)) {
298
                $status = $this->revokeRole($o, $rolename, 0, 'CASCADE');
299
                if ($status != 0) {
300
                    return -1;
301
                }
302
            }
303
        }
304
        return 0;
305
306
    }
307
308
    private function _dealWithOriginalMembers($original_members, $new_members_of_role, $rolename)
309
    {
310
        //members
311
        $old = explode(',', $original_members);
312
        foreach ($new_members_of_role as $m) {
313
            if (!in_array($m, $old, true)) {
314
                $status = $this->grantRole($rolename, $m);
315
                if ($status != 0) {
316
                    return -1;
317
                }
318
            }
319
        }
320
        if ($original_members) {
321
            foreach ($old as $o) {
322
                if (!in_array($o, $new_members_of_role, true)) {
323
                    $status = $this->revokeRole($rolename, $o, 0, 'CASCADE');
324
                    if ($status != 0) {
325
                        return -1;
326
                    }
327
                }
328
            }
329
        }
330
        return 0;
331
    }
332
333
    private function _dealWithOriginalAdmins($original_admins, $new_admins_of_role, $rolename)
334
    {
335
        $old = explode(',', $original_admins);
336
        foreach ($new_admins_of_role as $m) {
337
            if (!in_array($m, $old, true)) {
338
                $status = $this->grantRole($rolename, $m, 1);
339
                if ($status != 0) {
340
                    return -1;
341
                }
342
            }
343
        }
344
345
        foreach ($old as $o) {
346
            if (!in_array($o, $new_admins_of_role, true)) {
347
                $status = $this->revokeRole($rolename, $o, 1, 'CASCADE');
348
                if ($status != 0) {
349
                    return -1;
350
                }
351
            }
352
        }
353
        return 0;
354
    }
355
356
    private function _alterRole($rolename, $password, $connlimit, $expiry)
357
    {
358
        $enc = $this->_encryptPassword($rolename, $password);
359
        $this->clean($enc);
360
        $this->clean($connlimit);
361
        $this->clean($expiry);
362
363
        $sql = "ALTER ROLE \"{$rolename}\"";
364
        if ($password != '') {
365
            $sql .= " WITH ENCRYPTED PASSWORD '{$enc}'";
366
        }
367
368
        $sql .= $superuser ? ' SUPERUSER' : ' NOSUPERUSER';
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $superuser seems to be never defined.
Loading history...
369
        $sql .= $createdb ? ' CREATEDB' : ' NOCREATEDB';
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $createdb seems to be never defined.
Loading history...
370
        $sql .= $createrole ? ' CREATEROLE' : ' NOCREATEROLE';
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $createrole seems to be never defined.
Loading history...
371
        $sql .= $inherits ? ' INHERIT' : ' NOINHERIT';
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $inherits seems to be never defined.
Loading history...
372
        $sql .= $login ? ' LOGIN' : ' NOLOGIN';
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $login seems to be never defined.
Loading history...
373
        if ($connlimit != '') {
374
            $sql .= " CONNECTION LIMIT {$connlimit}";
375
        } else {
376
            $sql .= ' CONNECTION LIMIT -1';
377
        }
378
379
        if ($expiry != '') {
380
            $sql .= " VALID UNTIL '{$expiry}'";
381
        } else {
382
            $sql .= " VALID UNTIL 'infinity'";
383
        }
384
385
        return $this->execute($sql);
386
387
    }
388
389
    /**
390
     * Adjusts a role's info.
391
     *
392
     * @param string $rolename        The name of the role to adjust
393
     * @param string $password        A password for the role
394
     * @param bool   $superuser       Boolean whether or not the role is a superuser
395
     * @param bool   $createdb        Boolean whether or not the role can create databases
396
     * @param bool   $createrole      Boolean whether or not the role can create other roles
397
     * @param bool   $inherits        Boolean whether or not the role inherits the privileges from parent roles
398
     * @param bool   $login           Boolean whether or not the role will be allowed to login
399
     * @param number $connlimit       Number of concurrent connections the role can make
400
     * @param string $expiry          string Format 'YYYY-MM-DD HH:MM:SS'.  '' means never expire
401
     * @param array  $new_roles_to_add        (array) Roles to which the role will be immediately added as a new member
402
     * @param array  $new_members_of_role         (array) Roles which are automatically added as members of the role
403
     * @param array  $new_admins_of_role    (array) Roles which are automatically added as admin members of the role
404
     * @param string $original_parent_roles     Original roles whose the role belongs to, comma separated
405
     * @param string $original_members      Original roles that are members of the role, comma separated
406
     * @param string $original_admins Original roles that are admin members of the role, comma separated
407
     *
408
     * @return int 0 if operation was successful
409
     */
410
    public function setRole(
411
        $rolename,
412
        $password,
413
        $superuser,
0 ignored issues
show
Unused Code introduced by
The parameter $superuser is not used and could be removed. ( Ignorable by Annotation )

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

413
        /** @scrutinizer ignore-unused */ $superuser,

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
414
        $createdb,
0 ignored issues
show
Unused Code introduced by
The parameter $createdb is not used and could be removed. ( Ignorable by Annotation )

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

414
        /** @scrutinizer ignore-unused */ $createdb,

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
415
        $createrole,
0 ignored issues
show
Unused Code introduced by
The parameter $createrole is not used and could be removed. ( Ignorable by Annotation )

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

415
        /** @scrutinizer ignore-unused */ $createrole,

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
416
        $inherits,
0 ignored issues
show
Unused Code introduced by
The parameter $inherits is not used and could be removed. ( Ignorable by Annotation )

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

416
        /** @scrutinizer ignore-unused */ $inherits,

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
417
        $login,
0 ignored issues
show
Unused Code introduced by
The parameter $login is not used and could be removed. ( Ignorable by Annotation )

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

417
        /** @scrutinizer ignore-unused */ $login,

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
418
        $connlimit,
419
        $expiry,
420
        $new_roles_to_add,
421
        $new_members_of_role,
422
        $new_admins_of_role,
423
        $original_parent_roles,
424
        $original_members,
425
        $original_admins
426
    ) {
427
        $this->fieldClean($rolename);
428
429
        $this->fieldArrayClean($new_roles_to_add);
430
        $this->fieldArrayClean($new_members_of_role);
431
        $this->fieldArrayClean($new_admins_of_role);
432
433
        $status = $this->_alterRole($rolename, $password, $connlimit, $expiry);
434
        if ($status !== 0) {
435
            return -1;
436
        }
437
438
        // If there were existing users with the requested role,
439
        // assign their roles to the new user, and remove said
440
        // role from them if they are not among the new authorized members
441
        if ($original_parent_roles) {
442
            $status = $this->_dealWithOldParentRoles($original_parent_roles, $new_roles_to_add, $rolename);
443
            if ($status !== 0) {
444
                return -1;
445
            }
446
        }
447
448
        if ($original_members) {
449
            $status = $this->_dealWithOriginalMembers($original_members, $new_members_of_role, $rolename);
450
            if ($status !== 0) {
451
                return -1;
452
            }
453
        }
454
455
        if ($original_admins) {
456
            $status = $this->_dealWithOriginalAdmins($original_admins, $new_admins_of_role, $rolename);
457
            if ($status !== 0) {
458
                return -1;
459
            }
460
461
        }
462
463
        return $status;
464
    }
465
466
    /**
467
     * Grants membership in a role.
468
     *
469
     * @param string $role     The name of the target role
470
     * @param string $rolename The name of the role that will belong to the target role
471
     * @param int    $admin    (optional) Flag to grant the admin option
472
     *
473
     * @return int 0 if operation was successful
474
     */
475
    public function grantRole($role, $rolename, $admin = 0)
476
    {
477
        $this->fieldClean($role);
478
        $this->fieldClean($rolename);
479
480
        $sql = "GRANT \"{$role}\" TO \"{$rolename}\"";
481
        if ($admin == 1) {
482
            $sql .= ' WITH ADMIN OPTION';
483
        }
484
485
        return $this->execute($sql);
486
    }
487
488
    /**
489
     * Revokes membership in a role.
490
     *
491
     * @param string $role     The name of the target role
492
     * @param string $rolename The name of the role that will not belong to the target role
493
     * @param int    $admin    (optional) Flag to revoke only the admin option
494
     * @param string $type     (optional) Type of revoke: RESTRICT | CASCADE
495
     *
496
     * @return int 0 if operation was successful
497
     */
498
    public function revokeRole($role, $rolename, $admin = 0, $type = 'RESTRICT')
499
    {
500
        $this->fieldClean($role);
501
        $this->fieldClean($rolename);
502
503
        $sql = 'REVOKE ';
504
        if ($admin == 1) {
505
            $sql .= 'ADMIN OPTION FOR ';
506
        }
507
508
        $sql .= "\"{$role}\" FROM \"{$rolename}\" {$type}";
509
510
        return $this->execute($sql);
511
    }
512
513
    /**
514
     * Removes a role.
515
     *
516
     * @param string $rolename The name of the role to drop
517
     *
518
     * @return int 0 if operation was successful
519
     */
520
    public function dropRole($rolename)
521
    {
522
        $this->fieldClean($rolename);
523
524
        $sql = "DROP ROLE \"{$rolename}\"";
525
526
        return $this->execute($sql);
527
    }
528
529
    /**
530
     * Creates a new user.
531
     *
532
     * @param string $username   The username of the user to create
533
     * @param string $password   A password for the user
534
     * @param bool   $createdb   boolean Whether or not the user can create databases
535
     * @param bool   $createuser boolean Whether or not the user can create other users
536
     * @param string $expiry     string Format 'YYYY-MM-DD HH:MM:SS'.  '' means never expire
537
     * @param array  $groups     The groups to create the user in
538
     *
539
     * @return int 0 if operation was successful
540
     *
541
     * @internal param $group (array) The groups to create the user in
542
     */
543
    public function createUser($username, $password, $createdb, $createuser, $expiry, $groups)
544
    {
545
        $enc = $this->_encryptPassword($username, $password);
546
        $this->fieldClean($username);
547
        $this->clean($enc);
548
        $this->clean($expiry);
549
        $this->fieldArrayClean($groups);
550
551
        $sql = "CREATE USER \"{$username}\"";
552
        if ($password != '') {
553
            $sql .= " WITH ENCRYPTED PASSWORD '{$enc}'";
554
        }
555
556
        $sql .= $createdb ? ' CREATEDB' : ' NOCREATEDB';
557
        $sql .= $createuser ? ' CREATEUSER' : ' NOCREATEUSER';
558
        if (is_array($groups) && sizeof($groups) > 0) {
559
            $sql .= ' IN GROUP "' . join('", "', $groups) . '"';
560
        }
561
562
        if ($expiry != '') {
563
            $sql .= " VALID UNTIL '{$expiry}'";
564
        } else {
565
            $sql .= " VALID UNTIL 'infinity'";
566
        }
567
568
        return $this->execute($sql);
569
    }
570
571
    /**
572
     * Adjusts a user's info and renames the user.
573
     *
574
     * @param string $username   The username of the user to modify
575
     * @param string $password   A new password for the user
576
     * @param bool   $createdb   boolean Whether or not the user can create databases
577
     * @param bool   $createuser boolean Whether or not the user can create other users
578
     * @param string $expiry     string Format 'YYYY-MM-DD HH:MM:SS'.  '' means never expire.
579
     * @param string $newname    The new name of the user
580
     *
581
     * @return bool|int 0 success
582
     */
583
    public function setRenameUser($username, $password, $createdb, $createuser, $expiry, $newname)
584
    {
585
        $status = $this->beginTransaction();
586
        if ($status != 0) {
587
            return -1;
588
        }
589
590
        if ($username != $newname) {
591
            $status = $this->renameUser($username, $newname);
592
            if ($status != 0) {
593
                $this->rollbackTransaction();
594
595
                return -3;
596
            }
597
            $username = $newname;
598
        }
599
600
        $status = $this->setUser($username, $password, $createdb, $createuser, $expiry);
601
        if ($status != 0) {
602
            $this->rollbackTransaction();
603
604
            return -2;
605
        }
606
607
        return $this->endTransaction();
608
    }
609
610
    /**
611
     * Renames a user.
612
     *
613
     * @param string $username The username of the user to rename
614
     * @param string $newname  The new name of the user
615
     *
616
     * @return int 0 if operation was successful
617
     */
618
    public function renameUser($username, $newname)
619
    {
620
        $this->fieldClean($username);
621
        $this->fieldClean($newname);
622
623
        $sql = "ALTER USER \"{$username}\" RENAME TO \"{$newname}\"";
624
625
        return $this->execute($sql);
626
    }
627
628
    // Tablespace functions
629
630
    /**
631
     * Adjusts a user's info.
632
     *
633
     * @param string $username   The username of the user to modify
634
     * @param string $password   A new password for the user
635
     * @param bool   $createdb   boolean Whether or not the user can create databases
636
     * @param bool   $createuser boolean Whether or not the user can create other users
637
     * @param string $expiry     string Format 'YYYY-MM-DD HH:MM:SS'.  '' means never expire.
638
     *
639
     * @return int 0 if operation was successful
640
     */
641
    public function setUser($username, $password, $createdb, $createuser, $expiry)
642
    {
643
        $enc = $this->_encryptPassword($username, $password);
644
        $this->fieldClean($username);
645
        $this->clean($enc);
646
        $this->clean($expiry);
647
648
        $sql = "ALTER USER \"{$username}\"";
649
        if ($password != '') {
650
            $sql .= " WITH ENCRYPTED PASSWORD '{$enc}'";
651
        }
652
653
        $sql .= $createdb ? ' CREATEDB' : ' NOCREATEDB';
654
        $sql .= $createuser ? ' CREATEUSER' : ' NOCREATEUSER';
655
        if ($expiry != '') {
656
            $sql .= " VALID UNTIL '{$expiry}'";
657
        } else {
658
            $sql .= " VALID UNTIL 'infinity'";
659
        }
660
661
        return $this->execute($sql);
662
    }
663
664
    /**
665
     * Removes a user.
666
     *
667
     * @param string $username The username of the user to drop
668
     *
669
     * @return int 0 if operation was successful
670
     */
671
    public function dropUser($username)
672
    {
673
        $this->fieldClean($username);
674
675
        $sql = "DROP USER \"{$username}\"";
676
677
        return $this->execute($sql);
678
    }
679
680
    /**
681
     * Changes a role's password.
682
     *
683
     * @param string $rolename The role name
684
     * @param string $password The new password
685
     *
686
     * @return int 0 if operation was successful
687
     */
688
    public function changePassword($rolename, $password)
689
    {
690
        $enc = $this->_encryptPassword($rolename, $password);
691
        $this->fieldClean($rolename);
692
        $this->clean($enc);
693
694
        $sql = "ALTER ROLE \"{$rolename}\" WITH ENCRYPTED PASSWORD '{$enc}'";
695
696
        return $this->execute($sql);
697
    }
698
699
    /**
700
     * Adds a group member.
701
     *
702
     * @param string $groname The name of the group
703
     * @param string $user    The name of the user to add to the group
704
     *
705
     * @return int 0 if operation was successful
706
     */
707
    public function addGroupMember($groname, $user)
708
    {
709
        $this->fieldClean($groname);
710
        $this->fieldClean($user);
711
712
        $sql = "ALTER GROUP \"{$groname}\" ADD USER \"{$user}\"";
713
714
        return $this->execute($sql);
715
    }
716
717
    /**
718
     * Returns all role names which the role belongs to.
719
     *
720
     * @param string $rolename The role name
721
     *
722
     * @return \PHPPgAdmin\ADORecordSet All role names
723
     */
724
    public function getMemberOf($rolename)
725
    {
726
        $this->clean($rolename);
727
728
        $sql = "
729
			SELECT rolname FROM pg_catalog.pg_roles R, pg_auth_members M
730
			WHERE R.oid=M.roleid
731
				AND member IN (
732
					SELECT oid FROM pg_catalog.pg_roles
733
					WHERE rolname='{$rolename}')
734
			ORDER BY rolname";
735
736
        return $this->selectSet($sql);
737
    }
738
739
    // Administration functions
740
741
    /**
742
     * Returns all role names that are members of a role.
743
     *
744
     * @param string $rolename The role name
745
     * @param string $admin    (optional) Find only admin members
746
     *
747
     * @return \PHPPgAdmin\ADORecordSet All role names
748
     */
749
    public function getMembers($rolename, $admin = 'f')
750
    {
751
        $this->clean($rolename);
752
753
        $sql = "
754
			SELECT rolname FROM pg_catalog.pg_roles R, pg_auth_members M
755
			WHERE R.oid=M.member AND admin_option='{$admin}'
756
				AND roleid IN (SELECT oid FROM pg_catalog.pg_roles
757
					WHERE rolname='{$rolename}')
758
			ORDER BY rolname";
759
760
        return $this->selectSet($sql);
761
    }
762
763
    /**
764
     * Removes a group member.
765
     *
766
     * @param string $groname The name of the group
767
     * @param string $user    The name of the user to remove from the group
768
     *
769
     * @return int 0 if operation was successful
770
     */
771
    public function dropGroupMember($groname, $user)
772
    {
773
        $this->fieldClean($groname);
774
        $this->fieldClean($user);
775
776
        $sql = "ALTER GROUP \"{$groname}\" DROP USER \"{$user}\"";
777
778
        return $this->execute($sql);
779
    }
780
781
    /**
782
     * Return users in a specific group.
783
     *
784
     * @param string $groname The name of the group
785
     *
786
     * @return \PHPPgAdmin\ADORecordSet All users in the group
787
     */
788
    public function getGroup($groname)
789
    {
790
        $this->clean($groname);
791
792
        $sql = "
793
			SELECT s.usename FROM pg_catalog.pg_user s, pg_catalog.pg_group g
794
			WHERE g.groname='{$groname}' AND s.usesysid = ANY (g.grolist)
795
			ORDER BY s.usename";
796
797
        return $this->selectSet($sql);
798
    }
799
800
    /**
801
     * Returns all groups in the database cluser.
802
     *
803
     * @return \PHPPgAdmin\ADORecordSet All groups
804
     */
805
    public function getGroups()
806
    {
807
        $sql = 'SELECT groname FROM pg_group ORDER BY groname';
808
809
        return $this->selectSet($sql);
810
    }
811
812
    /**
813
     * Creates a new group.
814
     *
815
     * @param string $groname The name of the group
816
     * @param array  $users   An array of users to add to the group
817
     *
818
     * @return int 0 if operation was successful
819
     */
820
    public function createGroup($groname, $users)
821
    {
822
        $this->fieldClean($groname);
823
824
        $sql = "CREATE GROUP \"{$groname}\"";
825
826
        if (is_array($users) && sizeof($users) > 0) {
827
            $this->fieldArrayClean($users);
828
            $sql .= ' WITH USER "' . join('", "', $users) . '"';
829
        }
830
831
        return $this->execute($sql);
832
    }
833
834
    /**
835
     * Removes a group.
836
     *
837
     * @param string $groname The name of the group to drop
838
     *
839
     * @return int 0 if operation was successful
840
     */
841
    public function dropGroup($groname)
842
    {
843
        $this->fieldClean($groname);
844
845
        $sql = "DROP GROUP \"{$groname}\"";
846
847
        return $this->execute($sql);
848
    }
849
850
    /**
851
     * Grants a privilege to a user, group or public.
852
     *
853
     * @param string $mode        'GRANT' or 'REVOKE';
854
     * @param mixed  $type        The type of object
855
     * @param string $object      The name of the object
856
     * @param bool   $public      True to grant to public, false otherwise
857
     * @param mixed  $usernames   the array of usernames to grant privs to
858
     * @param mixed  $groupnames  the array of group names to grant privs to
859
     * @param mixed  $privileges  The array of privileges to grant (eg. ('SELECT', 'ALL PRIVILEGES', etc.) )
860
     * @param bool   $grantoption True if has grant option, false otherwise
861
     * @param bool   $cascade     True for cascade revoke, false otherwise
862
     * @param string $table       the column's table if type=column
863
     *
864
     * @return int 0 if operation was successful
865
     */
866
    public function setPrivileges(
867
        $mode,
868
        $type,
869
        $object,
870
        $public,
871
        $usernames,
872
        $groupnames,
873
        $privileges,
874
        $grantoption,
875
        $cascade,
876
        $table
877
    ) {
878
        $f_schema = $this->_schema;
879
        $this->fieldClean($f_schema);
880
        $this->fieldArrayClean($usernames);
881
        $this->fieldArrayClean($groupnames);
882
883
        // Input checking
884
        if (!is_array($privileges) || sizeof($privileges) == 0) {
885
            return -3;
886
        }
887
888
        if (!is_array($usernames) || !is_array($groupnames) ||
889
            (!$public && sizeof($usernames) == 0 && sizeof($groupnames) == 0)) {
890
            return -4;
891
        }
892
893
        if ($mode != 'GRANT' && $mode != 'REVOKE') {
894
            return -5;
895
        }
896
897
        $sql = $mode;
898
899
        // Grant option
900
        if ($this->hasGrantOption() && $mode == 'REVOKE' && $grantoption) {
901
            $sql .= ' GRANT OPTION FOR';
902
        }
903
904
        if (in_array('ALL PRIVILEGES', $privileges, true)) {
905
            $sql .= ' ALL PRIVILEGES';
906
        } else {
907
            if ($type == 'column') {
908
                $this->fieldClean($object);
909
                $sql .= ' ' . join(" (\"{$object}\"), ", $privileges);
910
            } else {
911
                $sql .= ' ' . join(', ', $privileges);
912
            }
913
        }
914
915
        switch ($type) {
916
            case 'column':
917
                $sql .= " (\"{$object}\")";
918
                $object = $table;
919
            // no break
920
            case 'table':
921
            case 'view':
922
            case 'sequence':
923
                $this->fieldClean($object);
924
                $sql .= " ON \"{$f_schema}\".\"{$object}\"";
925
926
                break;
927
            case 'database':
928
                $this->fieldClean($object);
929
                $sql .= " ON DATABASE \"{$object}\"";
930
931
                break;
932
            case 'function':
933
                // Function comes in with $object as function OID
934
                $fn = $this->getFunction($object);
935
                $this->fieldClean($fn->fields['proname']);
936
                $sql .= " ON FUNCTION \"{$f_schema}\".\"{$fn->fields['proname']}\"({$fn->fields['proarguments']})";
937
938
                break;
939
            case 'language':
940
                $this->fieldClean($object);
941
                $sql .= " ON LANGUAGE \"{$object}\"";
942
943
                break;
944
            case 'schema':
945
                $this->fieldClean($object);
946
                $sql .= " ON SCHEMA \"{$object}\"";
947
948
                break;
949
            case 'tablespace':
950
                $this->fieldClean($object);
951
                $sql .= " ON TABLESPACE \"{$object}\"";
952
953
                break;
954
            default:
955
                return -1;
956
        }
957
958
        // Dump PUBLIC
959
        $first = true;
960
        $sql .= ($mode == 'GRANT') ? ' TO ' : ' FROM ';
961
        if ($public) {
962
            $sql .= 'PUBLIC';
963
            $first = false;
964
        }
965
        // Dump users
966
        foreach ($usernames as $v) {
967
            if ($first) {
968
                $sql .= "\"{$v}\"";
969
                $first = false;
970
            } else {
971
                $sql .= ", \"{$v}\"";
972
            }
973
        }
974
        // Dump groups
975
        foreach ($groupnames as $v) {
976
            if ($first) {
977
                $sql .= "GROUP \"{$v}\"";
978
                $first = false;
979
            } else {
980
                $sql .= ", GROUP \"{$v}\"";
981
            }
982
        }
983
984
        // Grant option
985
        if ($this->hasGrantOption() && $mode == 'GRANT' && $grantoption) {
986
            $sql .= ' WITH GRANT OPTION';
987
        }
988
989
        // Cascade revoke
990
        if ($this->hasGrantOption() && $mode == 'REVOKE' && $cascade) {
991
            $sql .= ' CASCADE';
992
        }
993
994
        return $this->execute($sql);
995
    }
996
997
    abstract public function fieldClean(&$str);
998
999
    abstract public function beginTransaction();
1000
1001
    abstract public function rollbackTransaction();
1002
1003
    abstract public function endTransaction();
1004
1005
    abstract public function execute($sql);
1006
1007
    abstract public function setComment($obj_type, $obj_name, $table, $comment, $basetype = null);
1008
1009
    abstract public function selectSet($sql);
1010
1011
    abstract public function clean(&$str);
1012
1013
    abstract public function hasGrantOption();
1014
1015
    abstract public function getFunction($function_oid);
1016
1017
    abstract public function fieldArrayClean(&$arr);
1018
}
1019