Welcome to mirror list, hosted at ThFree Co, Russian Federation.

PrivilegesTrait.php « databasetraits « database « src - github.com/HuasoFoundries/phpPgAdmin6.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 823360c8d80c1c802f83f30499636e64d50cb06c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
<?php

/**
 * PHPPgAdmin 6.1.3
 */

namespace PHPPgAdmin\Database\Traits;

/**
 * Common trait for privileges manipulation.
 */
trait PrivilegesTrait
{
    /**
     * Grabs an array of users and their privileges for an object,
     * given its type.
     *
     * @param string      $object The name of the object whose privileges are to be retrieved
     * @param string      $type   The type of the object (eg. database, schema, relation, function or language)
     * @param null|string $table  Optional, column's table if type = column
     *
     * @return array|int Privileges array or error code
     *                   - -1         invalid type
     *                   - -2         object not found
     *                   - -3         unknown privilege type
     */
    public function getPrivileges($object, $type, $table = null)
    {
        $c_schema = $this->_schema;
        $this->clean($c_schema);
        $this->clean($object);

        switch ($type) {
            case 'column':
                $this->clean($table);
                $sql = "
                    SELECT E'{' || pg_catalog.array_to_string(attacl, E',') || E'}' as acl
                    FROM pg_catalog.pg_attribute a
                        LEFT JOIN pg_catalog.pg_class c ON (a.attrelid = c.oid)
                        LEFT JOIN pg_catalog.pg_namespace n ON (c.relnamespace=n.oid)
                    WHERE n.nspname='{$c_schema}'
                        AND c.relname='{$table}'
                        AND a.attname='{$object}'";

                break;
            case 'table':
            case 'view':
            case 'sequence':
                $sql = "
                    SELECT relacl AS acl FROM pg_catalog.pg_class
                    WHERE relname='{$object}'
                        AND relnamespace=(SELECT oid FROM pg_catalog.pg_namespace
                            WHERE nspname='{$c_schema}')";

                break;
            case 'database':
                $sql = "SELECT datacl AS acl FROM pg_catalog.pg_database WHERE datname='{$object}'";

                break;
            case 'function':
                // Since we fetch functions by oid, they are already constrained to
                // the current schema.
                $sql = "SELECT proacl AS acl FROM pg_catalog.pg_proc WHERE oid='{$object}'";

                break;
            case 'language':
                $sql = "SELECT lanacl AS acl FROM pg_catalog.pg_language WHERE lanname='{$object}'";

                break;
            case 'schema':
                $sql = "SELECT nspacl AS acl FROM pg_catalog.pg_namespace WHERE nspname='{$object}'";

                break;
            case 'tablespace':
                $sql = "SELECT spcacl AS acl FROM pg_catalog.pg_tablespace WHERE spcname='{$object}'";

                break;

            default:
                return -1;
        }

        // Fetch the ACL for object
        $acl = $this->selectField($sql, 'acl');

        if (-1 === $acl) {
            return -2;
        }

        if ('' === $acl || null === $acl || !(bool) $acl) {
            return [];
        }

        return $this->parseACL($acl);
    }

    /**
     * Grants a privilege to a user, group or public.
     *
     * @param string $mode        'GRANT' or 'REVOKE';
     * @param mixed  $type        The type of object
     * @param string $object      The name of the object
     * @param bool   $public      True to grant to public, false otherwise
     * @param mixed  $usernames   the array of usernames to grant privs to
     * @param mixed  $groupnames  the array of group names to grant privs to
     * @param mixed  $privileges  The array of privileges to grant (eg. ('SELECT', 'ALL PRIVILEGES', etc.) )
     * @param bool   $grantoption True if has grant option, false otherwise
     * @param bool   $cascade     True for cascade revoke, false otherwise
     * @param string $table       the column's table if type=column
     *
     * @return int|\PHPPgAdmin\ADORecordSet
     */
    public function setPrivileges(
        $mode,
        $type,
        $object,
        $public,
        $usernames,
        $groupnames,
        $privileges,
        $grantoption,
        $cascade,
        $table
    ) {
        $f_schema = $this->_schema;
        $this->fieldClean($f_schema);
        $this->fieldArrayClean($usernames);
        $this->fieldArrayClean($groupnames);

        // Input checking
        if (!\is_array($privileges) || 0 === \count($privileges)) {
            return -3;
        }

        if (!\is_array($usernames) || !\is_array($groupnames) ||
            (!$public && 0 === \count($usernames) && 0 === \count($groupnames))) {
            return -4;
        }

        if ('GRANT' !== $mode && 'REVOKE' !== $mode) {
            return -5;
        }

        $sql = $mode;

        // Grant option
        if ($this->hasGrantOption() && 'REVOKE' === $mode && $grantoption) {
            $sql .= ' GRANT OPTION FOR';
        }

        if (\in_array('ALL PRIVILEGES', $privileges, true)) {
            $sql .= ' ALL PRIVILEGES';
        } else {
            if ('column' === $type) {
                $this->fieldClean($object);
                $sql .= ' ' . \implode(" (\"{$object}\"), ", $privileges);
            } else {
                $sql .= ' ' . \implode(', ', $privileges);
            }
        }

        switch ($type) {
            case 'column':
                $sql .= " (\"{$object}\")";
                $object = $table;
            // no break
            case 'table':
            case 'view':
            case 'sequence':
                $this->fieldClean($object);
                $sql .= " ON \"{$f_schema}\".\"{$object}\"";

                break;
            case 'database':
                $this->fieldClean($object);
                $sql .= " ON DATABASE \"{$object}\"";

                break;
            case 'function':
                // Function comes in with $object as function OID
                $fn = $this->getFunction($object);
                $this->fieldClean($fn->fields['proname']);
                $sql .= " ON FUNCTION \"{$f_schema}\".\"{$fn->fields['proname']}\"({$fn->fields['proarguments']})";

                break;
            case 'language':
                $this->fieldClean($object);
                $sql .= " ON LANGUAGE \"{$object}\"";

                break;
            case 'schema':
                $this->fieldClean($object);
                $sql .= " ON SCHEMA \"{$object}\"";

                break;
            case 'tablespace':
                $this->fieldClean($object);
                $sql .= " ON TABLESPACE \"{$object}\"";

                break;

            default:
                return -1;
        }

        // Dump
        $first = true;
        $sql .= ('GRANT' === $mode) ? ' TO ' : ' FROM ';

        if ($public) {
            $sql .= 'PUBLIC';
            $first = false;
        }
        // Dump users
        foreach ($usernames as $v) {
            if ($first) {
                $sql .= "\"{$v}\"";
                $first = false;
            } else {
                $sql .= ", \"{$v}\"";
            }
        }
        // Dump groups
        foreach ($groupnames as $v) {
            if ($first) {
                $sql .= "GROUP \"{$v}\"";
                $first = false;
            } else {
                $sql .= ", GROUP \"{$v}\"";
            }
        }

        // Grant option
        if ($this->hasGrantOption() && 'GRANT' === $mode && $grantoption) {
            $sql .= ' WITH GRANT OPTION';
        }

        // Cascade revoke
        if ($this->hasGrantOption() && 'REVOKE' === $mode && $cascade) {
            $sql .= ' CASCADE';
        }

        return $this->execute($sql);
    }

    abstract public function fieldClean(&$str);

    abstract public function beginTransaction();

    abstract public function rollbackTransaction();

    abstract public function endTransaction();

    abstract public function execute($sql);

    abstract public function setComment($obj_type, $obj_name, $table, $comment, $basetype = null);

    abstract public function selectSet($sql);

    abstract public function clean(&$str);

    abstract public function hasGrantOption();

    abstract public function getFunction($function_oid);

    abstract public function fieldArrayClean(&$arr);

    abstract public function selectField($sql, $field);

    abstract public function hasRoles();

    /**
     * Internal function used for parsing ACLs.
     *
     * @param string $acl The ACL to parse (of type aclitem[])
     *
     * @return array|int Privileges array or integer with error code
     *
     * @internal bool $in_quotes toggles acl in_quotes attribute
     */
    protected function parseACL($acl)
    {
        // Take off the first and last characters (the braces)
        $acl = \mb_substr($acl, 1, \mb_strlen($acl) - 2);

        // Pick out individual ACE's by carefully parsing.  This is necessary in order
        // to cope with usernames and stuff that contain commas
        $aces = [];
        $i = $j = 0;
        $in_quotes = false;

        while (\mb_strlen($acl) > $i) {
            // If current char is a double quote and it's not escaped, then
            // enter quoted bit
            $char = \mb_substr($acl, $i, 1);

            if ('"' === $char && (0 === $i || '\\' !== \mb_substr($acl, $i - 1, 1))) {
                $in_quotes = !$in_quotes;
            } elseif (',' === $char && !$in_quotes) {
                // Add text so far to the array
                $aces[] = \mb_substr($acl, $j, $i - $j);
                $j = $i + 1;
            }
            ++$i;
        }
        // Add final text to the array
        $aces[] = \mb_substr($acl, $j);

        // Create the array to be returned
        $temp = [];

        // For each ACE, generate an entry in $temp
        foreach ($aces as $v) {
            // If the ACE begins with a double quote, strip them off both ends
            // and unescape backslashes and double quotes
            // $unquote = false;
            if (0 === \mb_strpos($v, '"')) {
                $v = \mb_substr($v, 1, \mb_strlen($v) - 2);
                $v = \str_replace('\\"', '"', $v);
                $v = \str_replace('\\\\', '\\', $v);
            }

            // Figure out type of ACE (public, user or group)
            if (0 === \mb_strpos($v, '=')) {
                $atype = 'public';
            } else {
                if ($this->hasRoles()) {
                    $atype = 'role';
                } else {
                    if (0 === \mb_strpos($v, 'group ')) {
                        $atype = 'group';
                        // Tear off 'group' prefix
                        $v = \mb_substr($v, 6);
                    } else {
                        $atype = 'user';
                    }
                }
            }

            // Break on unquoted equals sign...
            $i = 0;
            $in_quotes = false;
            $entity = null;
            $chars = null;

            while (\mb_strlen($v) > $i) {
                // If current char is a double quote and it's not escaped, then
                // enter quoted bit
                $char = \mb_substr($v, $i, 1);
                $next_char = \mb_substr($v, $i + 1, 1);

                if ('"' === $char && (0 === $i || '"' !== $next_char)) {
                    $in_quotes = !$in_quotes;
                } elseif ('"' === $char && '"' === $next_char) {
                    // Skip over escaped double quotes
                    ++$i;
                } elseif ('=' === $char && !$in_quotes) {
                    // Split on current equals sign
                    $entity = \mb_substr($v, 0, $i);
                    $chars = \mb_substr($v, $i + 1);

                    break;
                }
                ++$i;
            }

            // Check for quoting on entity name, and unescape if necessary
            if (0 === \mb_strpos($entity, '"')) {
                $entity = \mb_substr($entity, 1, \mb_strlen($entity) - 2);
                $entity = \str_replace('""', '"', $entity);
            }

            // New row to be added to $temp
            // (type, grantee, privileges, grantor, grant option?
            $row = [$atype, $entity, [], '', []];

            // Loop over chars and add privs to $row
            for ($i = 0; \mb_strlen($chars) > $i; ++$i) {
                // Append to row's privs list the string representing
                // the privilege
                $char = \mb_substr($chars, $i, 1);

                if ('*' === $char) {
                    $row[4][] = $this->privmap[\mb_substr($chars, $i - 1, 1)];
                } elseif ('/' === $char) {
                    $grantor = \mb_substr($chars, $i + 1);
                    // Check for quoting
                    if (0 === \mb_strpos($grantor, '"')) {
                        $grantor = \mb_substr($grantor, 1, \mb_strlen($grantor) - 2);
                        $grantor = \str_replace('""', '"', $grantor);
                    }
                    $row[3] = $grantor;

                    break;
                } else {
                    if (!isset($this->privmap[$char])) {
                        return -3;
                    }

                    $row[2][] = $this->privmap[$char];
                }
            }

            // Append row to temp
            $temp[] = $row;
        }

        return $temp;
    }
}