Coveralls logob
Coveralls logo
  • Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

php-casbin / database-adapter / 1550185875

7 Dec 2021 - 7:47 coverage: 94.194%. Remained the same
1550185875

Pull #21

github

GitHub
Merge 723c82634 into 3a91ed98b
Pull Request #21: Develop

10 of 11 new or added lines in 1 file covered. (90.91%)

4 existing lines in 1 file now uncovered.

146 of 155 relevant lines covered (94.19%)

82.88 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.19
/src/Adapter.php
1
<?php
2

3
namespace CasbinAdapter\Database;
4

5
use Casbin\Model\Model;
6
use Casbin\Persist\Adapter as AdapterContract;
7
use TechOne\Database\Manager;
8
use Casbin\Persist\AdapterHelper;
9
use Casbin\Persist\FilteredAdapter as FilteredAdapterContract;
10
use Casbin\Persist\Adapters\Filter;
11
use Casbin\Exceptions\InvalidFilterTypeException;
12
use Casbin\Persist\BatchAdapter as BatchAdapterContract;
13
use Casbin\Persist\UpdatableAdapter as UpdatableAdapterContract;
14
use Closure;
15
use Throwable;
16

17
/**
18
 * DatabaseAdapter.
19
 *
20
 * @author techlee@qq.com
21
 */
22
class Adapter implements AdapterContract, FilteredAdapterContract, BatchAdapterContract, UpdatableAdapterContract
23
{
24
    use AdapterHelper;
25

26
    protected $config;
27

28
    protected $filtered;
29

30
    protected $connection;
31

32
    public $casbinRuleTableName = 'casbin_rule';
33

34
    public $rows = [];
35

36
    public function __construct(array $config)
37
    {
38
        $this->config = $config;
330×
39
        $this->filtered = false;
330×
40
        $this->connection = (new Manager($config))->getConnection();
330×
41
        $this->initTable();
330×
42
    }
330×
43

44
    /**
45
     * Returns true if the loaded policy has been filtered.
46
     *
47
     * @return bool
48
     */
49
    public function isFiltered(): bool
50
    {
51
        return $this->filtered;
330×
52
    }
53

54
    /**
55
     * Sets filtered parameter.
56
     *
57
     * @param bool $filtered
58
     */
59
    public function setFiltered(bool $filtered): void
60
    {
61
        $this->filtered = $filtered;
30×
62
    }
30×
63

64
    /**
65
     * Filter the rule.
66
     *
67
     * @param array $rule
68
     * @return array
69
     */
70
    public function filterRule(array $rule): array
71
    {
72
        $rule = array_values($rule);
330×
73

74
        $i = count($rule) - 1;
330×
75
        for (; $i >= 0; $i--) {
330×
76
            if ($rule[$i] != '' && !is_null($rule[$i])) {
330×
77
                break;
330×
78
            }
79
        }
80

81
        return array_slice($rule, 0, $i + 1);
330×
82
    }
83

84
    public static function newAdapter(array $config)
85
    {
86
        return new static($config);
330×
87
    }
88

89
    public function initTable()
90
    {
91
        $sql = file_get_contents(__DIR__.'/../migrations/'.$this->config['type'].'.sql');
330×
92
        $sql = str_replace('%table_name%', $this->casbinRuleTableName, $sql);
330×
93
        $this->connection->execute($sql, []);
330×
94
    }
330×
95

96
    public function savePolicyLine($ptype, array $rule)
97
    {
98
        $col['ptype'] = $ptype;
90×
99
        foreach ($rule as $key => $value) {
90×
100
            $col['v'.strval($key).''] = $value;
90×
101
        }
102

103
        $colStr = implode(', ', array_keys($col));
90×
104

105
        $name = rtrim(str_repeat('?, ', count($col)), ', ');
90×
106

107
        $sql = 'INSERT INTO '.$this->casbinRuleTableName.'('.$colStr.') VALUES ('.$name.') ';
90×
108

109
        $this->connection->execute($sql, array_values($col));
90×
110
    }
90×
111

112
    /**
113
     * loads all policy rules from the storage.
114
     *
115
     * @param Model $model
116
     */
117
    public function loadPolicy(Model $model): void
118
    {
119
        $rows = $this->connection->query('SELECT ptype, v0, v1, v2, v3, v4, v5 FROM '.$this->casbinRuleTableName.'');
330×
120

121
        foreach ($rows as $row) {
330×
122
            $this->loadPolicyArray($this->filterRule($row), $model);
330×
123
        }
124
    }
330×
125

126
    /**
127
     * saves all policy rules to the storage.
128
     *
129
     * @param Model $model
130
     */
131
    public function savePolicy(Model $model): void
132
    {
133
        foreach ($model['p'] as $ptype => $ast) {
30×
134
            foreach ($ast->policy as $rule) {
30×
135
                $this->savePolicyLine($ptype, $rule);
30×
136
            }
137
        }
138

139
        foreach ($model['g'] as $ptype => $ast) {
30×
140
            foreach ($ast->policy as $rule) {
30×
141
                $this->savePolicyLine($ptype, $rule);
6×
142
            }
143
        }
144
    }
30×
145

146
    /**
147
     * adds a policy rule to the storage.
148
     * This is part of the Auto-Save feature.
149
     *
150
     * @param string $sec
151
     * @param string $ptype
152
     * @param array  $rule
153
     */
154
    public function addPolicy(string $sec, string $ptype, array $rule): void
155
    {
156
        $this->savePolicyLine($ptype, $rule);
60×
157
    }
60×
158

159
    public function addPolicies(string $sec, string $ptype, array $rules): void
160
    {
161
        $table = $this->casbinRuleTableName;
60×
162
        $columns = ['ptype', 'v0', 'v1', 'v2', 'v3', 'v4', 'v5'];
60×
163
        $values = [];
60×
164
        $sets = [];
60×
165
        $columnsCount = count($columns);
60×
166
        foreach ($rules as $rule) {
60×
167
            array_unshift($rule, $ptype);
60×
168
            $values = array_merge($values, array_pad($rule, $columnsCount, null));
60×
169
            $sets[] = array_pad([], $columnsCount, '?');
60×
170
        }
171
        $valuesStr = implode(', ', array_map(function ($set) {
172
            return '(' . implode(', ', $set) . ')';
60×
173
        }, $sets));
60×
174
        $sql = 'INSERT INTO ' . $table . ' (' . implode(', ', $columns) . ')' .
60×
175
            ' VALUES' . $valuesStr;
60×
176
        $this->connection->execute($sql, $values);
60×
177
    }
60×
178

179
    public function removePolicies(string $sec, string $ptype, array $rules): void
180
    {
181
        $this->connection->getPdo()->beginTransaction();
30×
182
        try {
183
            foreach ($rules as $rule) {
30×
184
                $this->removePolicy($sec, $ptype, $rule);
30×
185
            }
186
            $this->connection->getPdo()->commit();
30×
NEW
187
        } catch (Throwable $e) {
!
UNCOV
188
            $this->connection->getPdo()->rollback();
!
UNCOV
189
            throw $e;
!
190
        }
191
    }
30×
192

193
    /**
194
     * This is part of the Auto-Save feature.
195
     *
196
     * @param string $sec
197
     * @param string $ptype
198
     * @param array  $rule
199
     */
200
    public function removePolicy(string $sec, string $ptype, array $rule): void
201
    {
202
        $where['ptype'] = $ptype;
60×
203
        $condition[] = 'ptype = :ptype';
60×
204
        foreach ($rule as $key => $value) {
60×
205
            $where['v'.strval($key)] = $value;
60×
206
            $condition[] = 'v'.strval($key).' = :'.'v'.strval($key);
60×
207
        }
208

209
        $sql = 'DELETE FROM '.$this->casbinRuleTableName.' WHERE '.implode(' AND ', $condition);
60×
210

211
        $this->connection->execute($sql, $where);
60×
212
    }
60×
213

214
    /**
215
     * @param string $sec
216
     * @param string $ptype
217
     * @param int $fieldIndex
218
     * @param string|null ...$fieldValues
219
     * @return array
220
     * @throws Throwable
221
     */
222
    public function _removeFilteredPolicy(string $sec, string $ptype, int $fieldIndex, ?string ...$fieldValues): array
223
    {
224
        $removedRules = [];
60×
225
        $where['ptype'] = $ptype;
60×
226
        $condition[] = 'ptype = :ptype';
60×
227
        foreach (range(0, 5) as $value) {
60×
228
            if ($fieldIndex <= $value && $value < $fieldIndex + count($fieldValues)) {
60×
229
                if ('' != $fieldValues[$value - $fieldIndex]) {
60×
230
                    $where['v'.strval($value)] = $fieldValues[$value - $fieldIndex];
60×
231
                    $condition[] = 'v'.strval($value).' = :'.'v'.strval($value);
60×
232
                }
233
            }
234
        }
235

236
        $deleteSql = "DELETE FROM {$this->casbinRuleTableName} WHERE " . implode(' AND ', $condition);
60×
237

238
        $selectSql = "SELECT * FROM {$this->casbinRuleTableName} WHERE " . implode(' AND ', $condition);
60×
239

240
        $oldP = $this->connection->query($selectSql, $where);
60×
241
        foreach ($oldP as &$item) {
60×
242
            unset($item['ptype']);
60×
243
            unset($item['id']);
60×
244
            $item = $this->filterRule($item);
60×
245
            $removedRules[] = $item;
60×
246
        }
247

248
        $this->connection->execute($deleteSql, $where);
60×
249
        return $removedRules;
60×
250
    }
251

252
    /**
253
      * RemoveFilteredPolicy removes policy rules that match the filter from the storage.
254
      * This is part of the Auto-Save feature.
255
      *
256
      * @param string $sec
257
      * @param string $ptype
258
      * @param int $fieldIndex
259
      * @param string ...$fieldValues
260
      * @throws Exception|Throwable
261
      */
262
    public function removeFilteredPolicy(string $sec, string $ptype, int $fieldIndex, string ...$fieldValues): void
263
    {
264
        $this->_removeFilteredPolicy($sec, $ptype, $fieldIndex, ...$fieldValues);
30×
265
    }
30×
266

267
    /**
268
     * Loads only policy rules that match the filter from storage.
269
     *
270
     * @param Model $model
271
     * @param mixed $filter
272
     *
273
     * @throws CasbinException
274
     */
275
    public function loadFilteredPolicy(Model $model, $filter): void
276
    {
277
        // the basic sql
278
        $sql = 'SELECT ptype, v0, v1, v2, v3, v4, v5 FROM '.$this->casbinRuleTableName . ' WHERE ';
30×
279

280
        $bind = [];
30×
281

282
        if (is_string($filter)) {
30×
283
            $filter = str_replace(' ', '', $filter);
30×
284
            $filter = str_replace('\'', '', $filter);
30×
285
            $filter = explode('=', $filter);
30×
286
            $sql .= "$filter[0] = :{$filter[0]}";
30×
287
            $bind[$filter[0]] = $filter[1];
30×
288
        } elseif ($filter instanceof Filter) {
30×
289
            foreach ($filter->p as $k => $v) {
30×
290
                $where[] = $v . ' = :' . $v;
30×
291
                $bind[$v] = $filter->g[$k];
30×
292
            }
293
            $where = implode(' AND ', $where);
30×
294
            $sql .= $where;
30×
295
        } elseif ($filter instanceof Closure) {
30×
296
            $where = '';
30×
297
            $filter($where);
30×
298
            $where = str_replace(' ', '', $where);
30×
299
            $where = str_replace('\'', '', $where);
30×
300
            $where = explode('=', $where);
30×
301
            $sql .= "$where[0] = :{$where[0]}";
30×
302
            $bind[$where[0]] = $where[1];
30×
303
        } else {
304
            throw new InvalidFilterTypeException('invalid filter type');
30×
305
        }
306

307
        $rows = $this->connection->query($sql, $bind);
30×
308
        foreach ($rows as $row) {
30×
309
            $row = array_filter($row, function ($value) {
310
                return !is_null($value) && $value !== '';
30×
311
            });
30×
312
            unset($row['id']);
30×
313
            $line = implode(', ', array_filter($row, function ($val) {
314
                return '' != $val && !is_null($val);
30×
315
            }));
30×
316
            $this->loadPolicyLine(trim($line), $model);
30×
317
        }
318

319
        $this->setFiltered(true);
30×
320
    }
30×
321

322
    /**
323
     * Updates a policy rule from storage.
324
     * This is part of the Auto-Save feature.
325
     *
326
     * @param string $sec
327
     * @param string $ptype
328
     * @param string[] $oldRule
329
     * @param string[] $newPolicy
330
     */
331
    public function updatePolicy(string $sec, string $ptype, array $oldRule, array $newPolicy): void
332
    {
333
        $where['ptype'] = $ptype;
60×
334
        $condition[] = 'ptype = :ptype';
60×
335

336
        foreach ($oldRule as $key => $value) {
60×
337
            $placeholder = "w" . strval($key);
60×
338
            $where['w' . strval($key)] = $value;
60×
339
            $condition[] = 'v' . strval($key) . ' = :' . $placeholder;
60×
340
        }
341

342
        $update = [];
60×
343
        foreach ($newPolicy as $key => $value) {
60×
344
            $placeholder = "s" . strval($key);
60×
345
            $updateValue["$placeholder"] = $value;
60×
346
            $update[] = 'v' . strval($key) . ' = :' . $placeholder;
60×
347
        }
348

349
        $sql = "UPDATE {$this->casbinRuleTableName} SET " . implode(', ', $update) . " WHERE " . implode(' AND ', $condition);
60×
350

351
        $this->connection->execute($sql, array_merge($updateValue, $where));
60×
352
    }
60×
353

354
    /**
355
     * UpdatePolicies updates some policy rules to storage, like db, redis.
356
     *
357
     * @param string $sec
358
     * @param string $ptype
359
     * @param string[][] $oldRules
360
     * @param string[][] $newRules
361
     * @return void
362
     */
363
    public function updatePolicies(string $sec, string $ptype, array $oldRules, array $newRules): void
364
    {
365
        $this->connection->getPdo()->beginTransaction();
30×
366
        try {
367
            foreach ($oldRules as $i => $oldRule) {
30×
368
                $this->updatePolicy($sec, $ptype, $oldRule, $newRules[$i]);
30×
369
            }
370
            $this->connection->getPdo()->commit();
30×
371
        } catch (Throwable $e) {
!
372
            $this->connection->getPdo()->rollback();
!
UNCOV
373
            throw $e;
!
374
        }
375
    }
30×
376

377
    /**
378
     * @param string $sec
379
     * @param string $ptype
380
     * @param array $newRules
381
     * @param int $fieldIndex
382
     * @param string ...$fieldValues
383
     * @return array
384
     * @throws Throwable
385
     */
386
    public function updateFilteredPolicies(string $sec, string $ptype, array $newRules, int $fieldIndex, ?string ...$fieldValues): array
387
    {
388
        $oldRules = [];
30×
389
        $this->connection->getPdo()->beginTransaction();
30×
390
        try {
391
            $oldRules = $this->_removeFilteredPolicy($sec, $ptype, $fieldIndex, ...$fieldValues);
30×
392
            $this->addPolicies($sec, $ptype, $newRules);
30×
393
            $this->connection->getPdo()->commit();
30×
394
        } catch (Throwable $e) {
!
395
            $this->connection->getPdo()->rollback();
!
UNCOV
396
            throw $e;
!
397
        }
398

399
        return $oldRules;
30×
400
    }
401
}
Troubleshooting · Open an Issue · Sales · Support · ENTERPRISE · CAREERS · STATUS
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2023 Coveralls, Inc