Cancelled
Push — develop ( de9bfb...d662a5 )
by Felipe
06:08
created

PrivilegesTrait::parseACL()   F

Complexity

Conditions 26
Paths 1924

Size

Total Lines 123
Code Lines 67

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 123
rs 2
c 0
b 0
f 0
cc 26
eloc 67
nc 1924
nop 1

How to fix   Long Method    Complexity   

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:

1
<?php
2
3
/**
4
 * PHPPgAdmin v6.0.0-beta.47
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
            default:
79
                return -1;
80
        }
81
82
        // Fetch the ACL for object
83
        $acl = $this->selectField($sql, 'acl');
0 ignored issues
show
Bug introduced by
The method selectField() does not exist on PHPPgAdmin\Database\Traits\PrivilegesTrait. Did you maybe mean selectSet()? ( Ignorable by Annotation )

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

83
        /** @scrutinizer ignore-call */ 
84
        $acl = $this->selectField($sql, 'acl');

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
84
        if ($acl == -1) {
85
            return -2;
86
        }
87
88
        if ($acl == '' || $acl === null || !(bool) $acl) {
89
            return [];
90
        }
91
92
        return $this->parseACL($acl);
93
    }
94
95
    /**
96
     * Internal function used for parsing ACLs.
97
     *
98
     * @param string $acl The ACL to parse (of type aclitem[])
99
     *
100
     * @return array|int Privileges array or integer with error code
101
     *
102
     * @internal bool $in_quotes toggles acl in_quotes attribute
103
     */
104
    protected function parseACL($acl)
105
    {
106
        // Take off the first and last characters (the braces)
107
        $acl = substr($acl, 1, strlen($acl) - 2);
108
109
        // Pick out individual ACE's by carefully parsing.  This is necessary in order
110
        // to cope with usernames and stuff that contain commas
111
        $aces      = [];
112
        $i         = $j         = 0;
113
        $in_quotes = false;
114
        while ($i < strlen($acl)) {
115
            // If current char is a double quote and it's not escaped, then
116
            // enter quoted bit
117
            $char = substr($acl, $i, 1);
118
            if ($char == '"' && ($i == 0 || substr($acl, $i - 1, 1) != '\\')) {
119
                $in_quotes = !$in_quotes;
120
            } elseif ($char == ',' && !$in_quotes) {
121
                // Add text so far to the array
122
                $aces[] = substr($acl, $j, $i - $j);
123
                $j      = $i + 1;
124
            }
125
            ++$i;
126
        }
127
        // Add final text to the array
128
        $aces[] = substr($acl, $j);
129
130
        // Create the array to be returned
131
        $temp = [];
132
133
        // For each ACE, generate an entry in $temp
134
        foreach ($aces as $v) {
135
            // If the ACE begins with a double quote, strip them off both ends
136
            // and unescape backslashes and double quotes
137
            // $unquote = false;
138
            if (strpos($v, '"') === 0) {
139
                $v = substr($v, 1, strlen($v) - 2);
140
                $v = str_replace('\\"', '"', $v);
141
                $v = str_replace('\\\\', '\\', $v);
142
            }
143
144
            // Figure out type of ACE (public, user or group)
145
            if (strpos($v, '=') === 0) {
146
                $atype = 'public';
147
            } else {
148
                if ($this->hasRoles()) {
0 ignored issues
show
Bug introduced by
It seems like hasRoles() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

148
                if ($this->/** @scrutinizer ignore-call */ hasRoles()) {
Loading history...
149
                    $atype = 'role';
150
                } else {
151
                    if (strpos($v, 'group ') === 0) {
152
                        $atype = 'group';
153
                        // Tear off 'group' prefix
154
                        $v = substr($v, 6);
155
                    } else {
156
                        $atype = 'user';
157
                    }
158
                }
159
            }
160
161
            // Break on unquoted equals sign...
162
            $i         = 0;
163
            $in_quotes = false;
164
            $entity    = null;
165
            $chars     = null;
166
            while ($i < strlen($v)) {
167
                // If current char is a double quote and it's not escaped, then
168
                // enter quoted bit
169
                $char      = substr($v, $i, 1);
170
                $next_char = substr($v, $i + 1, 1);
171
                if ($char == '"' && ($i == 0 || $next_char != '"')) {
172
                    $in_quotes = !$in_quotes;
173
                } elseif ($char == '"' && $next_char == '"') {
174
                    // Skip over escaped double quotes
175
                    ++$i;
176
                } elseif ($char == '=' && !$in_quotes) {
177
                    // Split on current equals sign
178
                    $entity = substr($v, 0, $i);
179
                    $chars  = substr($v, $i + 1);
180
181
                    break;
182
                }
183
                ++$i;
184
            }
185
186
            // Check for quoting on entity name, and unescape if necessary
187
            if (strpos($entity, '"') === 0) {
188
                $entity = substr($entity, 1, strlen($entity) - 2);
189
                $entity = str_replace('""', '"', $entity);
190
            }
191
192
            // New row to be added to $temp
193
            // (type, grantee, privileges, grantor, grant option?
194
            $row = [$atype, $entity, [], '', []];
195
196
            // Loop over chars and add privs to $row
197
            for ($i = 0; $i < strlen($chars); ++$i) {
198
                // Append to row's privs list the string representing
199
                // the privilege
200
                $char = substr($chars, $i, 1);
201
                if ($char == '*') {
202
                    $row[4][] = $this->privmap[substr($chars, $i - 1, 1)];
203
                } elseif ($char == '/') {
204
                    $grantor = substr($chars, $i + 1);
205
                    // Check for quoting
206
                    if (strpos($grantor, '"') === 0) {
207
                        $grantor = substr($grantor, 1, strlen($grantor) - 2);
208
                        $grantor = str_replace('""', '"', $grantor);
209
                    }
210
                    $row[3] = $grantor;
211
212
                    break;
213
                } else {
214
                    if (!isset($this->privmap[$char])) {
215
                        return -3;
216
                    }
217
218
                    $row[2][] = $this->privmap[$char];
219
                }
220
            }
221
222
            // Append row to temp
223
            $temp[] = $row;
224
        }
225
226
        return $temp;
227
    }
228
229
    /**
230
     * Grants a privilege to a user, group or public.
231
     *
232
     * @param string $mode        'GRANT' or 'REVOKE';
233
     * @param mixed  $type        The type of object
234
     * @param string $object      The name of the object
235
     * @param bool   $public      True to grant to public, false otherwise
236
     * @param mixed  $usernames   the array of usernames to grant privs to
237
     * @param mixed  $groupnames  the array of group names to grant privs to
238
     * @param mixed  $privileges  The array of privileges to grant (eg. ('SELECT', 'ALL PRIVILEGES', etc.) )
239
     * @param bool   $grantoption True if has grant option, false otherwise
240
     * @param bool   $cascade     True for cascade revoke, false otherwise
241
     * @param string $table       the column's table if type=column
242
     *
243
     * @return int 0 if operation was successful
244
     */
245
    public function setPrivileges(
246
        $mode,
247
        $type,
248
        $object,
249
        $public,
250
        $usernames,
251
        $groupnames,
252
        $privileges,
253
        $grantoption,
254
        $cascade,
255
        $table
256
    ) {
257
        $f_schema = $this->_schema;
258
        $this->fieldClean($f_schema);
259
        $this->fieldArrayClean($usernames);
260
        $this->fieldArrayClean($groupnames);
261
262
        // Input checking
263
        if (!is_array($privileges) || sizeof($privileges) == 0) {
264
            return -3;
265
        }
266
267
        if (!is_array($usernames) || !is_array($groupnames) ||
268
            (!$public && sizeof($usernames) == 0 && sizeof($groupnames) == 0)) {
269
            return -4;
270
        }
271
272
        if ($mode != 'GRANT' && $mode != 'REVOKE') {
273
            return -5;
274
        }
275
276
        $sql = $mode;
277
278
        // Grant option
279
        if ($this->hasGrantOption() && $mode == 'REVOKE' && $grantoption) {
280
            $sql .= ' GRANT OPTION FOR';
281
        }
282
283
        if (in_array('ALL PRIVILEGES', $privileges, true)) {
284
            $sql .= ' ALL PRIVILEGES';
285
        } else {
286
            if ($type == 'column') {
287
                $this->fieldClean($object);
288
                $sql .= ' '.join(" (\"{$object}\"), ", $privileges);
289
            } else {
290
                $sql .= ' '.join(', ', $privileges);
291
            }
292
        }
293
294
        switch ($type) {
295
            case 'column':
296
                $sql .= " (\"{$object}\")";
297
                $object = $table;
298
            // no break
299
            case 'table':
300
            case 'view':
301
            case 'sequence':
302
                $this->fieldClean($object);
303
                $sql .= " ON \"{$f_schema}\".\"{$object}\"";
304
305
                break;
306
            case 'database':
307
                $this->fieldClean($object);
308
                $sql .= " ON DATABASE \"{$object}\"";
309
310
                break;
311
            case 'function':
312
                // Function comes in with $object as function OID
313
                $fn = $this->getFunction($object);
314
                $this->fieldClean($fn->fields['proname']);
315
                $sql .= " ON FUNCTION \"{$f_schema}\".\"{$fn->fields['proname']}\"({$fn->fields['proarguments']})";
316
317
                break;
318
            case 'language':
319
                $this->fieldClean($object);
320
                $sql .= " ON LANGUAGE \"{$object}\"";
321
322
                break;
323
            case 'schema':
324
                $this->fieldClean($object);
325
                $sql .= " ON SCHEMA \"{$object}\"";
326
327
                break;
328
            case 'tablespace':
329
                $this->fieldClean($object);
330
                $sql .= " ON TABLESPACE \"{$object}\"";
331
332
                break;
333
            default:
334
                return -1;
335
        }
336
337
        // Dump PUBLIC
338
        $first = true;
339
        $sql .= ($mode == 'GRANT') ? ' TO ' : ' FROM ';
340
        if ($public) {
341
            $sql .= 'PUBLIC';
342
            $first = false;
343
        }
344
        // Dump users
345
        foreach ($usernames as $v) {
346
            if ($first) {
347
                $sql .= "\"{$v}\"";
348
                $first = false;
349
            } else {
350
                $sql .= ", \"{$v}\"";
351
            }
352
        }
353
        // Dump groups
354
        foreach ($groupnames as $v) {
355
            if ($first) {
356
                $sql .= "GROUP \"{$v}\"";
357
                $first = false;
358
            } else {
359
                $sql .= ", GROUP \"{$v}\"";
360
            }
361
        }
362
363
        // Grant option
364
        if ($this->hasGrantOption() && $mode == 'GRANT' && $grantoption) {
365
            $sql .= ' WITH GRANT OPTION';
366
        }
367
368
        // Cascade revoke
369
        if ($this->hasGrantOption() && $mode == 'REVOKE' && $cascade) {
370
            $sql .= ' CASCADE';
371
        }
372
373
        return $this->execute($sql);
374
    }
375
376
    abstract public function fieldClean(&$str);
377
378
    abstract public function beginTransaction();
379
380
    abstract public function rollbackTransaction();
381
382
    abstract public function endTransaction();
383
384
    abstract public function execute($sql);
385
386
    abstract public function setComment($obj_type, $obj_name, $table, $comment, $basetype = null);
387
388
    abstract public function selectSet($sql);
389
390
    abstract public function clean(&$str);
391
392
    abstract public function hasGrantOption();
393
394
    abstract public function getFunction($function_oid);
395
396
    abstract public function fieldArrayClean(&$arr);
397
}
398