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 * Check if an entity has restrictions set on itself or its
47 * @param Entity $entity
51 public function checkIfRestrictionsSet(Entity $entity, $action)
53 $this->currentAction = $action;
54 if ($entity->isA('page')) {
55 return $entity->restricted || ($entity->chapter && $entity->chapter->restricted) || $entity->book->restricted;
56 } elseif ($entity->isA('chapter')) {
57 return $entity->restricted || $entity->book->restricted;
58 } elseif ($entity->isA('book')) {
59 return $entity->restricted;
64 * Add restrictions for a page query
66 * @param string $action
69 public function enforcePageRestrictions($query, $action = 'view')
71 // Prevent drafts being visible to others.
72 $query = $query->where(function ($query) {
73 $query->where('draft', '=', false);
74 if ($this->currentUser) {
75 $query->orWhere(function ($query) {
76 $query->where('draft', '=', true)->where('created_by', '=', $this->currentUser->id);
81 if ($this->isAdmin) return $query;
82 $this->currentAction = $action;
83 return $this->pageRestrictionQuery($query);
87 * The base query for restricting pages.
91 private function pageRestrictionQuery($query)
93 return $query->where(function ($parentWhereQuery) {
96 // (Book & chapter & page) or (Book & page & NO CHAPTER) unrestricted
97 ->where(function ($query) {
98 $query->where(function ($query) {
99 $query->whereExists(function ($query) {
100 $query->select('*')->from('chapters')
101 ->whereRaw('chapters.id=pages.chapter_id')
102 ->where('restricted', '=', false);
103 })->whereExists(function ($query) {
104 $query->select('*')->from('books')
105 ->whereRaw('books.id=pages.book_id')
106 ->where('restricted', '=', false);
107 })->where('restricted', '=', false);
108 })->orWhere(function ($query) {
109 $query->where('restricted', '=', false)->where('chapter_id', '=', 0)
110 ->whereExists(function ($query) {
111 $query->select('*')->from('books')
112 ->whereRaw('books.id=pages.book_id')
113 ->where('restricted', '=', false);
117 // Page unrestricted, Has no chapter & book has accepted restrictions
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');
124 ->whereExists(function ($query) {
125 $query->select('*')->from('books')
126 ->whereRaw('books.id=pages.book_id')
127 ->whereExists(function ($query) {
128 $this->checkRestrictionsQuery($query, 'books', 'Book');
132 // Page unrestricted, Has an unrestricted chapter & book has accepted restrictions
133 ->orWhere(function ($query) {
134 $query->where('restricted', '=', false)
135 ->whereExists(function ($query) {
136 $query->select('*')->from('chapters')
137 ->whereRaw('chapters.id=pages.chapter_id')->where('restricted', '=', false);
139 ->whereExists(function ($query) {
140 $query->select('*')->from('books')
141 ->whereRaw('books.id=pages.book_id')
142 ->whereExists(function ($query) {
143 $this->checkRestrictionsQuery($query, 'books', 'Book');
147 // Page unrestricted, Has a chapter with accepted permissions
148 ->orWhere(function ($query) {
149 $query->where('restricted', '=', false)
150 ->whereExists(function ($query) {
151 $query->select('*')->from('chapters')
152 ->whereRaw('chapters.id=pages.chapter_id')
153 ->where('restricted', '=', true)
154 ->whereExists(function ($query) {
155 $this->checkRestrictionsQuery($query, 'chapters', 'Chapter');
159 // Page has accepted permissions
160 ->orWhereExists(function ($query) {
161 $this->checkRestrictionsQuery($query, 'pages', 'Page');
167 * Add on permission restrictions to a chapter query.
169 * @param string $action
172 public function enforceChapterRestrictions($query, $action = 'view')
174 if ($this->isAdmin) return $query;
175 $this->currentAction = $action;
176 return $this->chapterRestrictionQuery($query);
180 * The base query for restricting chapters.
184 private function chapterRestrictionQuery($query)
186 return $query->where(function ($parentWhereQuery) {
189 // Book & chapter unrestricted
190 ->where(function ($query) {
191 $query->where('restricted', '=', false)->whereExists(function ($query) {
192 $query->select('*')->from('books')
193 ->whereRaw('books.id=chapters.book_id')
194 ->where('restricted', '=', false);
197 // Chapter unrestricted & book has accepted restrictions
198 ->orWhere(function ($query) {
199 $query->where('restricted', '=', false)
200 ->whereExists(function ($query) {
201 $query->select('*')->from('books')
202 ->whereRaw('books.id=chapters.book_id')
203 ->whereExists(function ($query) {
204 $this->checkRestrictionsQuery($query, 'books', 'Book');
208 // Chapter has accepted permissions
209 ->orWhereExists(function ($query) {
210 $this->checkRestrictionsQuery($query, 'chapters', 'Chapter');
216 * Add restrictions to a book query.
218 * @param string $action
221 public function enforceBookRestrictions($query, $action = 'view')
223 if ($this->isAdmin) return $query;
224 $this->currentAction = $action;
225 return $this->bookRestrictionQuery($query);
229 * The base query for restricting books.
233 private function bookRestrictionQuery($query)
235 return $query->where(function ($parentWhereQuery) {
237 ->where('restricted', '=', false)
238 ->orWhere(function ($query) {
239 $query->where('restricted', '=', true)->whereExists(function ($query) {
240 $this->checkRestrictionsQuery($query, 'books', 'Book');
247 * Filter items that have entities set a a polymorphic relation.
249 * @param string $tableName
250 * @param string $entityIdColumn
251 * @param string $entityTypeColumn
254 public function filterRestrictedEntityRelations($query, $tableName, $entityIdColumn, $entityTypeColumn)
256 if ($this->isAdmin) return $query;
257 $this->currentAction = 'view';
258 $tableDetails = ['tableName' => $tableName, 'entityIdColumn' => $entityIdColumn, 'entityTypeColumn' => $entityTypeColumn];
259 return $query->where(function ($query) use ($tableDetails) {
260 $query->where(function ($query) use (&$tableDetails) {
261 $query->where($tableDetails['entityTypeColumn'], '=', 'BookStack\Page')
262 ->whereExists(function ($query) use (&$tableDetails) {
263 $query->select('*')->from('pages')->whereRaw('pages.id=' . $tableDetails['tableName'] . '.' . $tableDetails['entityIdColumn'])
264 ->where(function ($query) {
265 $this->pageRestrictionQuery($query);
268 })->orWhere(function ($query) use (&$tableDetails) {
269 $query->where($tableDetails['entityTypeColumn'], '=', 'BookStack\Book')->whereExists(function ($query) use (&$tableDetails) {
270 $query->select('*')->from('books')->whereRaw('books.id=' . $tableDetails['tableName'] . '.' . $tableDetails['entityIdColumn'])
271 ->where(function ($query) {
272 $this->bookRestrictionQuery($query);
275 })->orWhere(function ($query) use (&$tableDetails) {
276 $query->where($tableDetails['entityTypeColumn'], '=', 'BookStack\Chapter')->whereExists(function ($query) use (&$tableDetails) {
277 $query->select('*')->from('chapters')->whereRaw('chapters.id=' . $tableDetails['tableName'] . '.' . $tableDetails['entityIdColumn'])
278 ->where(function ($query) {
279 $this->chapterRestrictionQuery($query);
287 * Filters pages that are a direct relation to another item.
290 * @param $entityIdColumn
293 public function filterRelatedPages($query, $tableName, $entityIdColumn)
295 if ($this->isAdmin) return $query;
296 $this->currentAction = 'view';
297 $tableDetails = ['tableName' => $tableName, 'entityIdColumn' => $entityIdColumn];
298 return $query->where(function ($query) use (&$tableDetails) {
299 $query->where(function ($query) use (&$tableDetails) {
300 $query->whereExists(function ($query) use (&$tableDetails) {
301 $query->select('*')->from('pages')->whereRaw('pages.id=' . $tableDetails['tableName'] . '.' . $tableDetails['entityIdColumn'])
302 ->where(function ($query) {
303 $this->pageRestrictionQuery($query);
305 })->orWhere($tableDetails['entityIdColumn'], '=', 0);
311 * The query to check the restrictions on an entity.
316 private function checkRestrictionsQuery($query, $tableName, $modelName)
318 $query->select('*')->from('restrictions')
319 ->whereRaw('restrictions.restrictable_id=' . $tableName . '.id')
320 ->where('restrictions.restrictable_type', '=', 'BookStack\\' . $modelName)
321 ->where('restrictions.action', '=', $this->currentAction)
322 ->whereIn('restrictions.role_id', $this->userRoles);