PrivilegesTrait::setPrivileges()   F
last analyzed

Complexity

Conditions 36
Paths 13839

Size

Total Lines 131
Code Lines 77

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 36
eloc 77
c 1
b 0
f 0
nc 13839
nop 10
dl 0
loc 131
rs 0

How to fix   Long Method    Complexity    Many Parameters   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
/**
4
 * PHPPgAdmin 6.1.3
5
 */
6
7
namespace PHPPgAdmin\Database\Traits;
8
9
/**
10
 * Common trait for privileges manipulation.
11
 */
12
trait PrivilegesTrait
13
{
14
    /**
15
     * Grabs an array of users and their privileges for an object,
16
     * given its type.
17
     *
18
     * @param string      $object The name of the object whose privileges are to be retrieved
19
     * @param string      $type   The type of the object (eg. database, schema, relation, function or language)
20
     * @param null|string $table  Optional, column's table if type = column
21
     *
22
     * @return array|int Privileges array or error code
23
     *                   - -1         invalid type
24
     *                   - -2         object not found
25
     *                   - -3         unknown privilege type
26
     */
27
    public function getPrivileges($object, $type, $table = null)
28
    {
29
        $c_schema = $this->_schema;
30
        $this->clean($c_schema);
31
        $this->clean($object);
32
33
        switch ($type) {
34
            case 'column':
35
                $this->clean($table);
36
                $sql = "
37
                    SELECT E'{' || pg_catalog.array_to_string(attacl, E',') || E'}' as acl
38
                    FROM pg_catalog.pg_attribute a
39
                        LEFT JOIN pg_catalog.pg_class c ON (a.attrelid = c.oid)
40
                        LEFT JOIN pg_catalog.pg_namespace n ON (c.relnamespace=n.oid)
41
                    WHERE n.nspname='{$c_schema}'
42
                        AND c.relname='{$table}'
43
                        AND a.attname='{$object}'";
44
45
                break;
46
            case 'table':
47
            case 'view':
48
            case 'sequence':
49
                $sql = "
50
                    SELECT relacl AS acl FROM pg_catalog.pg_class
51
                    WHERE relname='{$object}'
52
                        AND relnamespace=(SELECT oid FROM pg_catalog.pg_namespace
53
                            WHERE nspname='{$c_schema}')";
54
55
                break;
56
            case 'database':
57
                $sql = "SELECT datacl AS acl FROM pg_catalog.pg_database WHERE datname='{$object}'";
58
59
                break;
60
            case 'function':
61
                // Since we fetch functions by oid, they are already constrained to
62
                // the current schema.
63
                $sql = "SELECT proacl AS acl FROM pg_catalog.pg_proc WHERE oid='{$object}'";
64
65
                break;
66
            case 'language':
67
                $sql = "SELECT lanacl AS acl FROM pg_catalog.pg_language WHERE lanname='{$object}'";
68
69
                break;
70
            case 'schema':
71
                $sql = "SELECT nspacl AS acl FROM pg_catalog.pg_namespace WHERE nspname='{$object}'";
72
73
                break;
74
            case 'tablespace':
75
                $sql = "SELECT spcacl AS acl FROM pg_catalog.pg_tablespace WHERE spcname='{$object}'";
76
77
                break;
78
79
            default:
80
                return -1;
81
        }
82
83
        // Fetch the ACL for object
84
        $acl = $this->selectField($sql, 'acl');
85
86
        if (-1 === $acl) {
87
            return -2;
88
        }
89
90
        if ('' === $acl || null === $acl || !(bool) $acl) {
91
            return [];
92
        }
93
94
        return $this->parseACL($acl);
95
    }
96
97
    /**
98
     * Grants a privilege to a user, group or public.
99
     *
100
     * @param string $mode        'GRANT' or 'REVOKE';
101
     * @param mixed  $type        The type of object
102
     * @param string $object      The name of the object
103
     * @param bool   $public      True to grant to public, false otherwise
104
     * @param mixed  $usernames   the array of usernames to grant privs to
105
     * @param mixed  $groupnames  the array of group names to grant privs to
106
     * @param mixed  $privileges  The array of privileges to grant (eg. ('SELECT', 'ALL PRIVILEGES', etc.) )
107
     * @param bool   $grantoption True if has grant option, false otherwise
108
     * @param bool   $cascade     True for cascade revoke, false otherwise
109
     * @param string $table       the column's table if type=column
110
     *
111
     * @return int|\PHPPgAdmin\ADORecordSet
112
     */
113
    public function setPrivileges(
114
        $mode,
115
        $type,
116
        $object,
117
        $public,
118
        $usernames,
119
        $groupnames,
120
        $privileges,
121
        $grantoption,
122
        $cascade,
123
        $table
124
    ) {
125
        $f_schema = $this->_schema;
126
        $this->fieldClean($f_schema);
127
        $this->fieldArrayClean($usernames);
128
        $this->fieldArrayClean($groupnames);
129
130
        // Input checking
131
        if (!\is_array($privileges) || 0 === \count($privileges)) {
132
            return -3;
133
        }
134
135
        if (!\is_array($usernames) || !\is_array($groupnames) ||
136
            (!$public && 0 === \count($usernames) && 0 === \count($groupnames))) {
137
            return -4;
138
        }
139
140
        if ('GRANT' !== $mode && 'REVOKE' !== $mode) {
141
            return -5;
142
        }
143
144
        $sql = $mode;
145
146
        // Grant option
147
        if ($this->hasGrantOption() && 'REVOKE' === $mode && $grantoption) {
148
            $sql .= ' GRANT OPTION FOR';
149
        }
150
151
        if (\in_array('ALL PRIVILEGES', $privileges, true)) {
152
            $sql .= ' ALL PRIVILEGES';
153
        } else {
154
            if ('column' === $type) {
155
                $this->fieldClean($object);
156
                $sql .= ' ' . \implode(" (\"{$object}\"), ", $privileges);
157
            } else {
158
                $sql .= ' ' . \implode(', ', $privileges);
159
            }
160
        }
161
162
        switch ($type) {
163
            case 'column':
164
                $sql .= " (\"{$object}\")";
165
                $object = $table;
166
            // no break
167
            case 'table':
168
            case 'view':
169
            case 'sequence':
170
                $this->fieldClean($object);
171
                $sql .= " ON \"{$f_schema}\".\"{$object}\"";
172
173
                break;
174
            case 'database':
175
                $this->fieldClean($object);
176
                $sql .= " ON DATABASE \"{$object}\"";
177
178
                break;
179
            case 'function':
180
                // Function comes in with $object as function OID
181
                $fn = $this->getFunction($object);
182
                $this->fieldClean($fn->fields['proname']);
183
                $sql .= " ON FUNCTION \"{$f_schema}\".\"{$fn->fields['proname']}\"({$fn->fields['proarguments']})";
184
185
                break;
186
            case 'language':
187
                $this->fieldClean($object);
188
                $sql .= " ON LANGUAGE \"{$object}\"";
189
190
                break;
191
            case 'schema':
192
                $this->fieldClean($object);
193
                $sql .= " ON SCHEMA \"{$object}\"";
194
195
                break;
196
            case 'tablespace':
197
                $this->fieldClean($object);
198
                $sql .= " ON TABLESPACE \"{$object}\"";
199
200
                break;
201
202
            default:
203
                return -1;
204
        }
205
206
        // Dump
207
        $first = true;
208
        $sql .= ('GRANT' === $mode) ? ' TO ' : ' FROM ';
209
210
        if ($public) {
211
            $sql .= 'PUBLIC';
212
            $first = false;
213
        }
214
        // Dump users
215
        foreach ($usernames as $v) {
216
            if ($first) {
217
                $sql .= "\"{$v}\"";
218
                $first = false;
219
            } else {
220
                $sql .= ", \"{$v}\"";
221
            }
222
        }
223
        // Dump groups
224
        foreach ($groupnames as $v) {
225
            if ($first) {
226
                $sql .= "GROUP \"{$v}\"";
227
                $first = false;
228
            } else {
229
                $sql .= ", GROUP \"{$v}\"";
230
            }
231
        }
232
233
        // Grant option
234
        if ($this->hasGrantOption() && 'GRANT' === $mode && $grantoption) {
235
            $sql .= ' WITH GRANT OPTION';
236
        }
237
238
        // Cascade revoke
239
        if ($this->hasGrantOption() && 'REVOKE' === $mode && $cascade) {
240
            $sql .= ' CASCADE';
241
        }
242
243
        return $this->execute($sql);
244
    }
245
246
    abstract public function fieldClean(&$str);
247
248
    abstract public function beginTransaction();
249
250
    abstract public function rollbackTransaction();
251
252
    abstract public function endTransaction();
253
254
    abstract public function execute($sql);
255
256
    abstract public function setComment($obj_type, $obj_name, $table, $comment, $basetype = null);
257
258
    abstract public function selectSet($sql);
259
260
    abstract public function clean(&$str);
261
262
    abstract public function hasGrantOption();
263
264
    abstract public function getFunction($function_oid);
265
266
    abstract public function fieldArrayClean(&$arr);
267
268
    abstract public function selectField($sql, $field);
269
270
    abstract public function hasRoles();
271
272
    /**
273
     * Internal function used for parsing ACLs.
274
     *
275
     * @param string $acl The ACL to parse (of type aclitem[])
276
     *
277
     * @return array|int Privileges array or integer with error code
278
     *
279
     * @internal bool $in_quotes toggles acl in_quotes attribute
280
     */
281
    protected function parseACL($acl)
282
    {
283
        // Take off the first and last characters (the braces)
284
        $acl = \mb_substr($acl, 1, \mb_strlen($acl) - 2);
285
286
        // Pick out individual ACE's by carefully parsing.  This is necessary in order
287
        // to cope with usernames and stuff that contain commas
288
        $aces = [];
289
        $i = $j = 0;
290
        $in_quotes = false;
291
292
        while (\mb_strlen($acl) > $i) {
293
            // If current char is a double quote and it's not escaped, then
294
            // enter quoted bit
295
            $char = \mb_substr($acl, $i, 1);
296
297
            if ('"' === $char && (0 === $i || '\\' !== \mb_substr($acl, $i - 1, 1))) {
298
                $in_quotes = !$in_quotes;
299
            } elseif (',' === $char && !$in_quotes) {
300
                // Add text so far to the array
301
                $aces[] = \mb_substr($acl, $j, $i - $j);
302
                $j = $i + 1;
303
            }
304
            ++$i;
305
        }
306
        // Add final text to the array
307
        $aces[] = \mb_substr($acl, $j);
308
309
        // Create the array to be returned
310
        $temp = [];
311
312
        // For each ACE, generate an entry in $temp
313
        foreach ($aces as $v) {
314
            // If the ACE begins with a double quote, strip them off both ends
315
            // and unescape backslashes and double quotes
316
            // $unquote = false;
317
            if (0 === \mb_strpos($v, '"')) {
318
                $v = \mb_substr($v, 1, \mb_strlen($v) - 2);
319
                $v = \str_replace('\\"', '"', $v);
320
                $v = \str_replace('\\\\', '\\', $v);
321
            }
322
323
            // Figure out type of ACE (public, user or group)
324
            if (0 === \mb_strpos($v, '=')) {
325
                $atype = 'public';
326
            } else {
327
                if ($this->hasRoles()) {
328
                    $atype = 'role';
329
                } else {
330
                    if (0 === \mb_strpos($v, 'group ')) {
331
                        $atype = 'group';
332
                        // Tear off 'group' prefix
333
                        $v = \mb_substr($v, 6);
334
                    } else {
335
                        $atype = 'user';
336
                    }
337
                }
338
            }
339
340
            // Break on unquoted equals sign...
341
            $i = 0;
342
            $in_quotes = false;
343
            $entity = null;
344
            $chars = null;
345
346
            while (\mb_strlen($v) > $i) {
347
                // If current char is a double quote and it's not escaped, then
348
                // enter quoted bit
349
                $char = \mb_substr($v, $i, 1);
350
                $next_char = \mb_substr($v, $i + 1, 1);
351
352
                if ('"' === $char && (0 === $i || '"' !== $next_char)) {
353
                    $in_quotes = !$in_quotes;
354
                } elseif ('"' === $char && '"' === $next_char) {
355
                    // Skip over escaped double quotes
356
                    ++$i;
357
                } elseif ('=' === $char && !$in_quotes) {
358
                    // Split on current equals sign
359
                    $entity = \mb_substr($v, 0, $i);
360
                    $chars = \mb_substr($v, $i + 1);
361
362
                    break;
363
                }
364
                ++$i;
365
            }
366
367
            // Check for quoting on entity name, and unescape if necessary
368
            if (0 === \mb_strpos($entity, '"')) {
369
                $entity = \mb_substr($entity, 1, \mb_strlen($entity) - 2);
370
                $entity = \str_replace('""', '"', $entity);
371
            }
372
373
            // New row to be added to $temp
374
            // (type, grantee, privileges, grantor, grant option?
375
            $row = [$atype, $entity, [], '', []];
376
377
            // Loop over chars and add privs to $row
378
            for ($i = 0; \mb_strlen($chars) > $i; ++$i) {
379
                // Append to row's privs list the string representing
380
                // the privilege
381
                $char = \mb_substr($chars, $i, 1);
382
383
                if ('*' === $char) {
384
                    $row[4][] = $this->privmap[\mb_substr($chars, $i - 1, 1)];
385
                } elseif ('/' === $char) {
386
                    $grantor = \mb_substr($chars, $i + 1);
387
                    // Check for quoting
388
                    if (0 === \mb_strpos($grantor, '"')) {
389
                        $grantor = \mb_substr($grantor, 1, \mb_strlen($grantor) - 2);
390
                        $grantor = \str_replace('""', '"', $grantor);
391
                    }
392
                    $row[3] = $grantor;
393
394
                    break;
395
                } else {
396
                    if (!isset($this->privmap[$char])) {
397
                        return -3;
398
                    }
399
400
                    $row[2][] = $this->privmap[$char];
401
                }
402
            }
403
404
            // Append row to temp
405
            $temp[] = $row;
406
        }
407
408
        return $temp;
409
    }
410
}
411