1+ <?php
2+
3+ /**
4+ * @desc Policy Adapter
5+ * @author Tinywan(ShaoBo Wan)
6+ * @date 2022/01/12 10:37
7+ */
8+
9+ declare (strict_types=1 );
10+
11+ namespace Casbin \WebmanPermission \Adapter ;
12+
13+ use Casbin \Model \Model ;
14+ use Casbin \Persist \Adapter ;
15+ use Casbin \Persist \AdapterHelper ;
16+ use Casbin \Persist \UpdatableAdapter ;
17+ use Casbin \Persist \BatchAdapter ;
18+ use Casbin \Persist \FilteredAdapter ;
19+ use Casbin \Persist \Adapters \Filter ;
20+ use Casbin \Exceptions \InvalidFilterTypeException ;
21+ use Casbin \WebmanPermission \Model \LaravelRuleModel ;
22+ use Casbin \WebmanPermission \Model \RuleModel ;
23+ use Illuminate \Support \Facades \DB ;
24+
25+ /**
26+ * DatabaseAdapter.
27+ *
28+ 29+ */
30+ class LaravelDatabaseAdapter implements Adapter, UpdatableAdapter, BatchAdapter, FilteredAdapter
31+ {
32+ use AdapterHelper;
33+
34+ /**
35+ * @var bool
36+ */
37+ private bool $ filtered = false ;
38+
39+ /**
40+ * RuleModel model.
41+ *
42+ * @var LaravelRuleModel
43+ */
44+ protected $ model ;
45+
46+ /**
47+ * the DatabaseAdapter constructor.
48+ *
49+ * @param RuleModel $model
50+ */
51+ public function __construct (RuleModel $ model )
52+ {
53+ $ this ->model = $ model ;
54+ }
55+
56+ /**
57+ * savePolicyLine function.
58+ *
59+ * @param string $ptype
60+ * @param array $rule
61+ *
62+ * @return void
63+ */
64+ public function savePolicyLine (string $ ptype , array $ rule )
65+ {
66+ $ col ['ptype ' ] = $ ptype ;
67+ foreach ($ rule as $ key => $ value ) {
68+ $ col ['v ' . strval ($ key ) . '' ] = $ value ;
69+ }
70+ $ this ->model ->create ($ col );
71+ }
72+
73+ /**
74+ * loads all policy rules from the storage.
75+ *
76+ * @param Model $model
77+ */
78+ public function loadPolicy (Model $ model ): void
79+ {
80+ $ rows = $ this ->model ->getAllFromCache ();
81+ foreach ($ rows as $ row ) {
82+ $ line = implode (', ' , array_filter (array_slice ($ row , 1 ), function ($ val ) {
83+ return '' != $ val && !is_null ($ val );
84+ }));
85+ $ this ->loadPolicyLine (trim ($ line ), $ model );
86+ }
87+ }
88+
89+ /**
90+ * saves all policy rules to the storage.
91+ *
92+ * @param Model $model
93+ */
94+ public function savePolicy (Model $ model ): void
95+ {
96+ foreach ($ model ['p ' ] as $ ptype => $ ast ) {
97+ foreach ($ ast ->policy as $ rule ) {
98+ $ this ->savePolicyLine ($ ptype , $ rule );
99+ }
100+ }
101+
102+ foreach ($ model ['g ' ] as $ ptype => $ ast ) {
103+ foreach ($ ast ->policy as $ rule ) {
104+ $ this ->savePolicyLine ($ ptype , $ rule );
105+ }
106+ }
107+ }
108+
109+ /**
110+ * adds a policy rule to the storage.
111+ * This is part of the Auto-Save feature.
112+ *
113+ * @param string $sec
114+ * @param string $ptype
115+ * @param array $rule
116+ */
117+ public function addPolicy (string $ sec , string $ ptype , array $ rule ): void
118+ {
119+ $ this ->savePolicyLine ($ ptype , $ rule );
120+ }
121+
122+ /**
123+ * Adds a policy rules to the storage.
124+ * This is part of the Auto-Save feature.
125+ *
126+ * @param string $sec
127+ * @param string $ptype
128+ * @param string[][] $rules
129+ */
130+ public function addPolicies (string $ sec , string $ ptype , array $ rules ): void
131+ {
132+ $ cols = [];
133+ $ i = 0 ;
134+
135+ foreach ($ rules as $ rule ) {
136+ $ temp ['ptype ' ] = $ ptype ;
137+ $ temp ['created_at ' ] = date ("Y-m-d h:m:i " );
138+ $ temp ['updated_at ' ] = $ temp ['created_at ' ];
139+ foreach ($ rule as $ key => $ value ) {
140+ $ temp ['v ' . strval ($ key )] = $ value ;
141+ }
142+ $ cols [$ i ++] = $ temp ?? [];
143+ $ temp = [];
144+ }
145+ $ this ->model ->insert ($ cols );
146+ LaravelRuleModel::fireModelEvent ('saved ' );
147+ }
148+
149+ /**
150+ * This is part of the Auto-Save feature.
151+ *
152+ * @param string $sec
153+ * @param string $ptype
154+ * @param array $rule
155+ */
156+ public function removePolicy (string $ sec , string $ ptype , array $ rule ): void
157+ {
158+ $ instance = $ this ->model ->where ('ptype ' , $ ptype );
159+ foreach ($ rule as $ key => $ value ) {
160+ $ instance ->where ('v ' . strval ($ key ), $ value );
161+ }
162+ $ instance ->delete ();
163+ LaravelRuleModel::fireModelEvent ('deleted ' );
164+ }
165+
166+ /**
167+ * Removes policy rules from the storage.
168+ * This is part of the Auto-Save feature.
169+ *
170+ * @param string $sec
171+ * @param string $ptype
172+ * @param string[][] $rules
173+ */
174+ public function removePolicies (string $ sec , string $ ptype , array $ rules ): void
175+ {
176+ DB ::transaction (function () use ($ sec , $ ptype , $ rules ) {
177+ foreach ($ rules as $ rule ) {
178+ $ this ->removePolicy ($ sec , $ ptype , $ rule );
179+ }
180+ });
181+ }
182+
183+ /**
184+ * RemoveFilteredPolicy removes policy rules that match the filter from the storage.
185+ * This is part of the Auto-Save feature.
186+ *
187+ * @param string $sec
188+ * @param string $ptype
189+ * @param int $fieldIndex
190+ * @param string ...$fieldValues
191+ */
192+ public function removeFilteredPolicy (string $ sec , string $ ptype , int $ fieldIndex , string ...$ fieldValues ): void
193+ {
194+ $ instance = $ this ->model ->where ('ptype ' , $ ptype );
195+ foreach (range (0 , 5 ) as $ value ) {
196+ if ($ fieldIndex <= $ value && $ value < $ fieldIndex + count ($ fieldValues )) {
197+ if ('' != $ fieldValues [$ value - $ fieldIndex ]) {
198+ $ instance ->where ('v ' . strval ($ value ), $ fieldValues [$ value - $ fieldIndex ]);
199+ }
200+ }
201+ }
202+
203+ $ oldP = $ instance ->get ()->makeHidden (['created_at ' ,'updated_at ' , 'id ' , 'ptype ' ])->toArray ();
204+ foreach ($ oldP as &$ item ) {
205+ $ item = $ this ->filterRule ($ item );
206+ $ removedRules [] = $ item ;
207+ }
208+ $ instance ->delete ();
209+ LaravelRuleModel::fireModelEvent ('deleted ' );
210+ }
211+
212+ /**
213+ * Updates a policy rule from storage.
214+ * This is part of the Auto-Save feature.
215+ *
216+ * @param string $sec
217+ * @param string $ptype
218+ * @param string[] $oldRule
219+ * @param string[] $newPolicy
220+ */
221+ public function updatePolicy (string $ sec , string $ ptype , array $ oldRule , array $ newPolicy ): void
222+ {
223+ $ instance = $ this ->model ->where ('ptype ' , $ ptype );
224+ foreach ($ oldRule as $ key => $ value ) {
225+ $ instance ->where ('v ' . strval ($ key ), $ value );
226+ }
227+ $ instance = $ instance ->first ();
228+
229+ $ update = [];
230+ foreach ($ newPolicy as $ key => $ value ) {
231+ $ update ['v ' . $ key ] = $ value ;
232+ }
233+
234+ $ instance ->update ($ update );
235+ LaravelRuleModel::fireModelEvent ('saved ' );
236+ }
237+
238+ /**
239+ * UpdatePolicies updates some policy rules to storage, like DB, redis.
240+ *
241+ * @param string $sec
242+ * @param string $ptype
243+ * @param string[][] $oldRules
244+ * @param string[][] $newRules
245+ * @return void
246+ */
247+ public function updatePolicies (string $ sec , string $ ptype , array $ oldRules , array $ newRules ): void
248+ {
249+ DB ::transaction (function () use ($ sec , $ ptype , $ oldRules , $ newRules ) {
250+ foreach ($ oldRules as $ i => $ oldRule ) {
251+ $ this ->updatePolicy ($ sec , $ ptype , $ oldRule , $ newRules [$ i ]);
252+ }
253+ });
254+ }
255+
256+ /**
257+ * UpdateFilteredPolicies deletes old rules and adds new rules.
258+ *
259+ * @param string $sec
260+ * @param string $ptype
261+ * @param array $newPolicies
262+ * @param integer $fieldIndex
263+ * @param string ...$fieldValues
264+ * @return array
265+ */
266+ public function updateFilteredPolicies (string $ sec , string $ ptype , array $ newPolicies , int $ fieldIndex , string ...$ fieldValues ): array
267+ {
268+ $ oldRules = [];
269+ \Illuminate \Support \Facades \DB ::transaction (function () use ($ sec , $ ptype , $ fieldIndex , $ fieldValues , $ newPolicies , &$ oldRules ) {
270+ $ oldRules = $ this ->_removeFilteredPolicy ($ sec , $ ptype , $ fieldIndex , ...$ fieldValues );
271+ $ this ->addPolicies ($ sec , $ ptype , $ newPolicies );
272+ });
273+ return $ oldRules ;
274+ }
275+
276+ /**
277+ * Returns true if the loaded policy has been filtered.
278+ *
279+ * @return bool
280+ */
281+ public function isFiltered (): bool
282+ {
283+ return $ this ->filtered ;
284+ }
285+
286+ /**
287+ * Sets filtered parameter.
288+ *
289+ * @param bool $filtered
290+ */
291+ public function setFiltered (bool $ filtered ): void
292+ {
293+ $ this ->filtered = $ filtered ;
294+ }
295+
296+ /**
297+ * Loads only policy rules that match the filter.
298+ *
299+ * @param Model $model
300+ * @param mixed $filter
301+ */
302+ public function loadFilteredPolicy (Model $ model , $ filter ): void
303+ {
304+ $ instance = $ this ->model ;
305+ if (is_string ($ filter )) {
306+ $ instance = $ instance ->whereRaw ($ filter );
307+ } elseif ($ filter instanceof Filter) {
308+ foreach ($ filter ->p as $ k => $ v ) {
309+ $ where [$ v ] = $ filter ->g [$ k ];
310+ $ instance = $ instance ->where ($ v , $ filter ->g [$ k ]);
311+ }
312+ } elseif ($ filter instanceof \Closure) {
313+ $ instance = $ instance ->where ($ filter );
314+ } else {
315+ throw new InvalidFilterTypeException ('invalid filter type ' );
316+ }
317+ $ rows = $ instance ->get ()->makeHidden (['created_at ' ,'updated_at ' , 'id ' ])->toArray ();
318+ foreach ($ rows as $ row ) {
319+ $ row = array_filter ($ row , function ($ value ) { return !is_null ($ value ) && $ value !== '' ; });
320+ $ line = implode (', ' , array_filter ($ row , function ($ val ) {
321+ return '' != $ val && !is_null ($ val );
322+ }));
323+ $ this ->loadPolicyLine (trim ($ line ), $ model );
324+ }
325+ $ this ->setFiltered (true );
326+ }
327+ }
0 commit comments