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

PrivilegesTrait::setPrivileges()   F

Complexity

Conditions 36
Paths 13839

Size

Total Lines 129
Code Lines 77

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 129
rs 2
c 0
b 0
f 0
cc 36
eloc 77
nc 13839
nop 10

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 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