1 <?php namespace BookStack\Services;
5 class RestrictionService
10 protected $currentAction;
11 protected $currentUser;
14 * RestrictionService constructor.
16 public function __construct()
18 $this->currentUser = auth()->user();
19 $this->userRoles = $this->currentUser ? $this->currentUser->roles->pluck('id') : [];
20 $this->isAdmin = $this->currentUser ? $this->currentUser->hasRole('admin') : false;
24 * Checks if an entity has a restriction set upon it.
25 * @param Entity $entity
29 public function checkIfEntityRestricted(Entity $entity, $action)
31 if ($this->isAdmin) return true;
32 $this->currentAction = $action;
33 $baseQuery = $entity->where('id', '=', $entity->id);
34 if ($entity->isA('page')) {
35 return $this->pageRestrictionQuery($baseQuery)->count() > 0;
36 } elseif ($entity->isA('chapter')) {
37 return $this->chapterRestrictionQuery($baseQuery)->count() > 0;
38 } elseif ($entity->isA('book')) {
39 return $this->bookRestrictionQuery($baseQuery)->count() > 0;
45 * Add restrictions for a page query
47 * @param string $action
50 public function enforcePageRestrictions($query, $action = 'view')
52 // Prevent drafts being visible to others.
53 $query = $query->where(function ($query) {
54 $query->where('draft', '=', false);
55 if ($this->currentUser) {
56 $query->orWhere(function ($query) {
57 $query->where('draft', '=', true)->where('created_by', '=', $this->currentUser->id);
62 if ($this->isAdmin) return $query;
63 $this->currentAction = $action;
64 return $this->pageRestrictionQuery($query);
68 * The base query for restricting pages.
72 private function pageRestrictionQuery($query)
74 return $query->where(function ($parentWhereQuery) {
77 // (Book & chapter & page) or (Book & page & NO CHAPTER) unrestricted
78 ->where(function ($query) {
79 $query->where(function ($query) {
80 $query->whereExists(function ($query) {
81 $query->select('*')->from('chapters')
82 ->whereRaw('chapters.id=pages.chapter_id')
83 ->where('restricted', '=', false);
84 })->whereExists(function ($query) {
85 $query->select('*')->from('books')
86 ->whereRaw('books.id=pages.book_id')
87 ->where('restricted', '=', false);
88 })->where('restricted', '=', false);
89 })->orWhere(function ($query) {
90 $query->where('restricted', '=', false)->where('chapter_id', '=', 0)
91 ->whereExists(function ($query) {
92 $query->select('*')->from('books')
93 ->whereRaw('books.id=pages.book_id')
94 ->where('restricted', '=', false);
98 // Page unrestricted, Has no chapter & book has accepted restrictions
99 ->orWhere(function ($query) {
100 $query->where('restricted', '=', false)
101 ->whereExists(function ($query) {
102 $query->select('*')->from('chapters')
103 ->whereRaw('chapters.id=pages.chapter_id');
105 ->whereExists(function ($query) {
106 $query->select('*')->from('books')
107 ->whereRaw('books.id=pages.book_id')
108 ->whereExists(function ($query) {
109 $this->checkRestrictionsQuery($query, 'books', 'Book');
113 // Page unrestricted, Has an unrestricted chapter & book has accepted restrictions
114 ->orWhere(function ($query) {
115 $query->where('restricted', '=', false)
116 ->whereExists(function ($query) {
117 $query->select('*')->from('chapters')
118 ->whereRaw('chapters.id=pages.chapter_id')->where('restricted', '=', false);
120 ->whereExists(function ($query) {
121 $query->select('*')->from('books')
122 ->whereRaw('books.id=pages.book_id')
123 ->whereExists(function ($query) {
124 $this->checkRestrictionsQuery($query, 'books', 'Book');
128 // Page unrestricted, Has a chapter with accepted permissions
129 ->orWhere(function ($query) {
130 $query->where('restricted', '=', false)
131 ->whereExists(function ($query) {
132 $query->select('*')->from('chapters')
133 ->whereRaw('chapters.id=pages.chapter_id')
134 ->where('restricted', '=', true)
135 ->whereExists(function ($query) {
136 $this->checkRestrictionsQuery($query, 'chapters', 'Chapter');
140 // Page has accepted permissions
141 ->orWhereExists(function ($query) {
142 $this->checkRestrictionsQuery($query, 'pages', 'Page');
148 * Add on permission restrictions to a chapter query.
150 * @param string $action
153 public function enforceChapterRestrictions($query, $action = 'view')
155 if ($this->isAdmin) return $query;
156 $this->currentAction = $action;
157 return $this->chapterRestrictionQuery($query);
161 * The base query for restricting chapters.
165 private function chapterRestrictionQuery($query)
167 return $query->where(function ($parentWhereQuery) {
170 // Book & chapter unrestricted
171 ->where(function ($query) {
172 $query->where('restricted', '=', false)->whereExists(function ($query) {
173 $query->select('*')->from('books')
174 ->whereRaw('books.id=chapters.book_id')
175 ->where('restricted', '=', false);
178 // Chapter unrestricted & book has accepted restrictions
179 ->orWhere(function ($query) {
180 $query->where('restricted', '=', false)
181 ->whereExists(function ($query) {
182 $query->select('*')->from('books')
183 ->whereRaw('books.id=chapters.book_id')
184 ->whereExists(function ($query) {
185 $this->checkRestrictionsQuery($query, 'books', 'Book');
189 // Chapter has accepted permissions
190 ->orWhereExists(function ($query) {
191 $this->checkRestrictionsQuery($query, 'chapters', 'Chapter');
197 * Add restrictions to a book query.
199 * @param string $action
202 public function enforceBookRestrictions($query, $action = 'view')
204 if ($this->isAdmin) return $query;
205 $this->currentAction = $action;
206 return $this->bookRestrictionQuery($query);
210 * The base query for restricting books.
214 private function bookRestrictionQuery($query)
216 return $query->where(function ($parentWhereQuery) {
218 ->where('restricted', '=', false)
219 ->orWhere(function ($query) {
220 $query->where('restricted', '=', true)->whereExists(function ($query) {
221 $this->checkRestrictionsQuery($query, 'books', 'Book');
228 * Filter items that have entities set a a polymorphic relation.
230 * @param string $tableName
231 * @param string $entityIdColumn
232 * @param string $entityTypeColumn
235 public function filterRestrictedEntityRelations($query, $tableName, $entityIdColumn, $entityTypeColumn)
237 if ($this->isAdmin) return $query;
238 $this->currentAction = 'view';
239 $tableDetails = ['tableName' => $tableName, 'entityIdColumn' => $entityIdColumn, 'entityTypeColumn' => $entityTypeColumn];
240 return $query->where(function ($query) use ($tableDetails) {
241 $query->where(function ($query) use (&$tableDetails) {
242 $query->where($tableDetails['entityTypeColumn'], '=', 'BookStack\Page')
243 ->whereExists(function ($query) use (&$tableDetails) {
244 $query->select('*')->from('pages')->whereRaw('pages.id=' . $tableDetails['tableName'] . '.' . $tableDetails['entityIdColumn'])
245 ->where(function ($query) {
246 $this->pageRestrictionQuery($query);
249 })->orWhere(function ($query) use (&$tableDetails) {
250 $query->where($tableDetails['entityTypeColumn'], '=', 'BookStack\Book')->whereExists(function ($query) use (&$tableDetails) {
251 $query->select('*')->from('books')->whereRaw('books.id=' . $tableDetails['tableName'] . '.' . $tableDetails['entityIdColumn'])
252 ->where(function ($query) {
253 $this->bookRestrictionQuery($query);
256 })->orWhere(function ($query) use (&$tableDetails) {
257 $query->where($tableDetails['entityTypeColumn'], '=', 'BookStack\Chapter')->whereExists(function ($query) use (&$tableDetails) {
258 $query->select('*')->from('chapters')->whereRaw('chapters.id=' . $tableDetails['tableName'] . '.' . $tableDetails['entityIdColumn'])
259 ->where(function ($query) {
260 $this->chapterRestrictionQuery($query);
268 * Filters pages that are a direct relation to another item.
271 * @param $entityIdColumn
274 public function filterRelatedPages($query, $tableName, $entityIdColumn)
276 if ($this->isAdmin) return $query;
277 $this->currentAction = 'view';
278 $tableDetails = ['tableName' => $tableName, 'entityIdColumn' => $entityIdColumn];
279 return $query->where(function ($query) use (&$tableDetails) {
280 $query->where(function ($query) use (&$tableDetails) {
281 $query->whereExists(function ($query) use (&$tableDetails) {
282 $query->select('*')->from('pages')->whereRaw('pages.id=' . $tableDetails['tableName'] . '.' . $tableDetails['entityIdColumn'])
283 ->where(function ($query) {
284 $this->pageRestrictionQuery($query);
286 })->orWhere($tableDetails['entityIdColumn'], '=', 0);
292 * The query to check the restrictions on an entity.
297 private function checkRestrictionsQuery($query, $tableName, $modelName)
299 $query->select('*')->from('restrictions')
300 ->whereRaw('restrictions.restrictable_id=' . $tableName . '.id')
301 ->where('restrictions.restrictable_type', '=', 'BookStack\\' . $modelName)
302 ->where('restrictions.action', '=', $this->currentAction)
303 ->whereIn('restrictions.role_id', $this->userRoles);