]> BookStack Code Mirror - bookstack/blob - app/Services/RestrictionService.php
Merge branch 'custom_role_system'
[bookstack] / app / Services / RestrictionService.php
1 <?php namespace BookStack\Services;
2
3 use BookStack\Entity;
4
5 class RestrictionService
6 {
7
8     protected $userRoles;
9     protected $isAdmin;
10     protected $currentAction;
11
12     /**
13      * RestrictionService constructor.
14      */
15     public function __construct()
16     {
17         $user = auth()->user();
18         $this->userRoles = $user ? auth()->user()->roles->pluck('id') : [];
19         $this->isAdmin = $user ? auth()->user()->hasRole('admin') : false;
20     }
21
22     /**
23      * Checks if an entity has a restriction set upon it.
24      * @param Entity $entity
25      * @param $action
26      * @return bool
27      */
28     public function checkIfEntityRestricted(Entity $entity, $action)
29     {
30         if ($this->isAdmin) return true;
31         $this->currentAction = $action;
32         $baseQuery = $entity->where('id', '=', $entity->id);
33         if ($entity->isA('page')) {
34             return $this->pageRestrictionQuery($baseQuery)->count() > 0;
35         } elseif ($entity->isA('chapter')) {
36             return $this->chapterRestrictionQuery($baseQuery)->count() > 0;
37         } elseif ($entity->isA('book')) {
38             return $this->bookRestrictionQuery($baseQuery)->count() > 0;
39         }
40         return false;
41     }
42
43     /**
44      * Add restrictions for a page query
45      * @param $query
46      * @param string $action
47      * @return mixed
48      */
49     public function enforcePageRestrictions($query, $action = 'view')
50     {
51         if ($this->isAdmin) return $query;
52         $this->currentAction = $action;
53         return $this->pageRestrictionQuery($query);
54     }
55
56     /**
57      * The base query for restricting pages.
58      * @param $query
59      * @return mixed
60      */
61     private function pageRestrictionQuery($query)
62     {
63         return $query->where(function ($parentWhereQuery) {
64
65             $parentWhereQuery
66                 // (Book & chapter & page) or (Book & page & NO CHAPTER) unrestricted
67                 ->where(function ($query) {
68                     $query->where(function ($query) {
69                         $query->whereExists(function ($query) {
70                             $query->select('*')->from('chapters')
71                                 ->whereRaw('chapters.id=pages.chapter_id')
72                                 ->where('restricted', '=', false);
73                         })->whereExists(function ($query) {
74                             $query->select('*')->from('books')
75                                 ->whereRaw('books.id=pages.book_id')
76                                 ->where('restricted', '=', false);
77                         })->where('restricted', '=', false);
78                     })->orWhere(function ($query) {
79                         $query->where('restricted', '=', false)->where('chapter_id', '=', 0)
80                             ->whereExists(function ($query) {
81                                 $query->select('*')->from('books')
82                                     ->whereRaw('books.id=pages.book_id')
83                                     ->where('restricted', '=', false);
84                             });
85                     });
86                 })
87                 // Page unrestricted, Has no chapter & book has accepted restrictions
88                 ->orWhere(function ($query) {
89                     $query->where('restricted', '=', false)
90                         ->whereExists(function ($query) {
91                             $query->select('*')->from('chapters')
92                                 ->whereRaw('chapters.id=pages.chapter_id');
93                         }, 'and', true)
94                         ->whereExists(function ($query) {
95                             $query->select('*')->from('books')
96                                 ->whereRaw('books.id=pages.book_id')
97                                 ->whereExists(function ($query) {
98                                     $this->checkRestrictionsQuery($query, 'books', 'Book');
99                                 });
100                         });
101                 })
102                 // Page unrestricted, Has an unrestricted chapter & book has accepted restrictions
103                 ->orWhere(function ($query) {
104                     $query->where('restricted', '=', false)
105                         ->whereExists(function ($query) {
106                             $query->select('*')->from('chapters')
107                                 ->whereRaw('chapters.id=pages.chapter_id')->where('restricted', '=', false);
108                         })
109                         ->whereExists(function ($query) {
110                             $query->select('*')->from('books')
111                                 ->whereRaw('books.id=pages.book_id')
112                                 ->whereExists(function ($query) {
113                                     $this->checkRestrictionsQuery($query, 'books', 'Book');
114                                 });
115                         });
116                 })
117                 // Page unrestricted, Has a chapter with accepted permissions
118                 ->orWhere(function ($query) {
119                     $query->where('restricted', '=', false)
120                         ->whereExists(function ($query) {
121                             $query->select('*')->from('chapters')
122                                 ->whereRaw('chapters.id=pages.chapter_id')
123                                 ->where('restricted', '=', true)
124                                 ->whereExists(function ($query) {
125                                     $this->checkRestrictionsQuery($query, 'chapters', 'Chapter');
126                                 });
127                         });
128                 })
129                 // Page has accepted permissions
130                 ->orWhereExists(function ($query) {
131                     $this->checkRestrictionsQuery($query, 'pages', 'Page');
132                 });
133         });
134     }
135
136     /**
137      * Add on permission restrictions to a chapter query.
138      * @param $query
139      * @param string $action
140      * @return mixed
141      */
142     public function enforceChapterRestrictions($query, $action = 'view')
143     {
144         if ($this->isAdmin) return $query;
145         $this->currentAction = $action;
146         return $this->chapterRestrictionQuery($query);
147     }
148
149     /**
150      * The base query for restricting chapters.
151      * @param $query
152      * @return mixed
153      */
154     private function chapterRestrictionQuery($query)
155     {
156         return $query->where(function ($parentWhereQuery) {
157
158             $parentWhereQuery
159                 // Book & chapter unrestricted
160                 ->where(function ($query) {
161                     $query->where('restricted', '=', false)->whereExists(function ($query) {
162                         $query->select('*')->from('books')
163                             ->whereRaw('books.id=chapters.book_id')
164                             ->where('restricted', '=', false);
165                     });
166                 })
167                 // Chapter unrestricted & book has accepted restrictions
168                 ->orWhere(function ($query) {
169                     $query->where('restricted', '=', false)
170                         ->whereExists(function ($query) {
171                             $query->select('*')->from('books')
172                                 ->whereRaw('books.id=chapters.book_id')
173                                 ->whereExists(function ($query) {
174                                     $this->checkRestrictionsQuery($query, 'books', 'Book');
175                                 });
176                         });
177                 })
178                 // Chapter has accepted permissions
179                 ->orWhereExists(function ($query) {
180                     $this->checkRestrictionsQuery($query, 'chapters', 'Chapter');
181                 });
182         });
183     }
184
185     /**
186      * Add restrictions to a book query.
187      * @param $query
188      * @param string $action
189      * @return mixed
190      */
191     public function enforceBookRestrictions($query, $action = 'view')
192     {
193         if ($this->isAdmin) return $query;
194         $this->currentAction = $action;
195         return $this->bookRestrictionQuery($query);
196     }
197
198     /**
199      * The base query for restricting books.
200      * @param $query
201      * @return mixed
202      */
203     private function bookRestrictionQuery($query)
204     {
205         return $query->where(function ($parentWhereQuery) {
206             $parentWhereQuery
207                 ->where('restricted', '=', false)
208                 ->orWhere(function ($query) {
209                     $query->where('restricted', '=', true)->whereExists(function ($query) {
210                         $this->checkRestrictionsQuery($query, 'books', 'Book');
211                     });
212                 });
213         });
214     }
215
216     /**
217      * Filter items that have entities set a a polymorphic relation.
218      * @param $query
219      * @param string $tableName
220      * @param string $entityIdColumn
221      * @param string $entityTypeColumn
222      * @return mixed
223      */
224     public function filterRestrictedEntityRelations($query, $tableName, $entityIdColumn, $entityTypeColumn)
225     {
226         if ($this->isAdmin) return $query;
227         $this->currentAction = 'view';
228         $tableDetails = ['tableName' => $tableName, 'entityIdColumn' => $entityIdColumn, 'entityTypeColumn' => $entityTypeColumn];
229         return $query->where(function ($query) use ($tableDetails) {
230             $query->where(function ($query) use (&$tableDetails) {
231                 $query->where($tableDetails['entityTypeColumn'], '=', 'BookStack\Page')
232                     ->whereExists(function ($query) use (&$tableDetails) {
233                         $query->select('*')->from('pages')->whereRaw('pages.id=' . $tableDetails['tableName'] . '.' . $tableDetails['entityIdColumn'])
234                             ->where(function ($query) {
235                                 $this->pageRestrictionQuery($query);
236                             });
237                     });
238             })->orWhere(function ($query) use (&$tableDetails) {
239                 $query->where($tableDetails['entityTypeColumn'], '=', 'BookStack\Book')->whereExists(function ($query) use (&$tableDetails) {
240                     $query->select('*')->from('books')->whereRaw('books.id=' . $tableDetails['tableName'] . '.' . $tableDetails['entityIdColumn'])
241                         ->where(function ($query) {
242                             $this->bookRestrictionQuery($query);
243                         });
244                 });
245             })->orWhere(function ($query) use (&$tableDetails) {
246                 $query->where($tableDetails['entityTypeColumn'], '=', 'BookStack\Chapter')->whereExists(function ($query) use (&$tableDetails) {
247                     $query->select('*')->from('chapters')->whereRaw('chapters.id=' . $tableDetails['tableName'] . '.' . $tableDetails['entityIdColumn'])
248                         ->where(function ($query) {
249                             $this->chapterRestrictionQuery($query);
250                         });
251                 });
252             });
253         });
254     }
255
256     /**
257      * The query to check the restrictions on an entity.
258      * @param $query
259      * @param $tableName
260      * @param $modelName
261      */
262     private function checkRestrictionsQuery($query, $tableName, $modelName)
263     {
264         $query->select('*')->from('restrictions')
265             ->whereRaw('restrictions.restrictable_id=' . $tableName . '.id')
266             ->where('restrictions.restrictable_type', '=', 'BookStack\\' . $modelName)
267             ->where('restrictions.action', '=', $this->currentAction)
268             ->whereIn('restrictions.role_id', $this->userRoles);
269     }
270
271
272 }