Passed
Push — develop ( 7c0cd9...49abac )
by Felipe
04:38
created

RoleTrait::_dealWithOldParentRoles()   B

Complexity

Conditions 7
Paths 13

Size

Total Lines 21
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

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