Failed Conditions
Push — php8 ( f7c5b0...7c6ccd )
by Simon
15:01
created
includes/Fragments/NavigationMenuAccessControl.php 2 patches
Indentation   +92 added lines, -92 removed lines patch added patch discarded remove patch
@@ -37,108 +37,108 @@
 block discarded – undo
37 37
 
38 38
 trait NavigationMenuAccessControl
39 39
 {
40
-    protected abstract function assign($name, $value);
40
+	protected abstract function assign($name, $value);
41 41
 
42
-    protected abstract function getSecurityManager(): ISecurityManager;
42
+	protected abstract function getSecurityManager(): ISecurityManager;
43 43
 
44
-    public abstract function getDomainAccessManager(): IDomainAccessManager;
44
+	public abstract function getDomainAccessManager(): IDomainAccessManager;
45 45
 
46
-    /**
47
-     * @param $currentUser
48
-     */
49
-    protected function setupNavMenuAccess($currentUser)
50
-    {
51
-        $this->assign('nav__canRequests', $this->getSecurityManager()
52
-                ->allows(PageMain::class, RoleConfigurationBase::MAIN, $currentUser) === ISecurityManager::ALLOWED);
46
+	/**
47
+	 * @param $currentUser
48
+	 */
49
+	protected function setupNavMenuAccess($currentUser)
50
+	{
51
+		$this->assign('nav__canRequests', $this->getSecurityManager()
52
+				->allows(PageMain::class, RoleConfigurationBase::MAIN, $currentUser) === ISecurityManager::ALLOWED);
53 53
 
54
-        $this->assign('nav__canLogs', $this->getSecurityManager()
55
-                ->allows(PageLog::class, RoleConfigurationBase::MAIN, $currentUser) === ISecurityManager::ALLOWED);
56
-        $this->assign('nav__canUsers', $this->getSecurityManager()
57
-                ->allows(StatsUsers::class, RoleConfigurationBase::MAIN, $currentUser) === ISecurityManager::ALLOWED);
58
-        $this->assign('nav__canSearch', $this->getSecurityManager()
59
-                ->allows(PageSearch::class, RoleConfigurationBase::MAIN, $currentUser) === ISecurityManager::ALLOWED);
60
-        $this->assign('nav__canStats', $this->getSecurityManager()
61
-                ->allows(StatsMain::class, RoleConfigurationBase::MAIN, $currentUser) === ISecurityManager::ALLOWED);
54
+		$this->assign('nav__canLogs', $this->getSecurityManager()
55
+				->allows(PageLog::class, RoleConfigurationBase::MAIN, $currentUser) === ISecurityManager::ALLOWED);
56
+		$this->assign('nav__canUsers', $this->getSecurityManager()
57
+				->allows(StatsUsers::class, RoleConfigurationBase::MAIN, $currentUser) === ISecurityManager::ALLOWED);
58
+		$this->assign('nav__canSearch', $this->getSecurityManager()
59
+				->allows(PageSearch::class, RoleConfigurationBase::MAIN, $currentUser) === ISecurityManager::ALLOWED);
60
+		$this->assign('nav__canStats', $this->getSecurityManager()
61
+				->allows(StatsMain::class, RoleConfigurationBase::MAIN, $currentUser) === ISecurityManager::ALLOWED);
62 62
 
63
-        $this->assign('nav__canBan', $this->getSecurityManager()
64
-                ->allows(PageBan::class, RoleConfigurationBase::MAIN, $currentUser) === ISecurityManager::ALLOWED);
65
-        $this->assign('nav__canEmailMgmt', $this->getSecurityManager()
66
-                ->allows(PageEmailManagement::class, RoleConfigurationBase::MAIN,
67
-                    $currentUser) === ISecurityManager::ALLOWED);
68
-        $this->assign('nav__canWelcomeMgmt', $this->getSecurityManager()
69
-                ->allows(PageWelcomeTemplateManagement::class, RoleConfigurationBase::MAIN,
70
-                    $currentUser) === ISecurityManager::ALLOWED);
71
-        $this->assign('nav__canSiteNoticeMgmt', $this->getSecurityManager()
72
-                ->allows(PageSiteNotice::class, RoleConfigurationBase::MAIN, $currentUser) === ISecurityManager::ALLOWED);
73
-        $this->assign('nav__canUserMgmt', $this->getSecurityManager()
74
-                ->allows(PageUserManagement::class, RoleConfigurationBase::MAIN,
75
-                    $currentUser) === ISecurityManager::ALLOWED);
76
-        $this->assign('nav__canJobQueue', $this->getSecurityManager()
77
-                ->allows(PageJobQueue::class, RoleConfigurationBase::MAIN,
78
-                    $currentUser) === ISecurityManager::ALLOWED);
79
-        $this->assign('nav__canDomainMgmt', $this->getSecurityManager()
80
-                ->allows(PageDomainManagement::class, RoleConfigurationBase::MAIN,
81
-                    $currentUser) === ISecurityManager::ALLOWED);
82
-        $this->assign('nav__canFlaggedComments', $this->getSecurityManager()
83
-                ->allows(PageListFlaggedComments::class, RoleConfigurationBase::MAIN,
84
-                    $currentUser) === ISecurityManager::ALLOWED);
85
-        $this->assign('nav__canQueueMgmt', $this->getSecurityManager()
86
-                ->allows(PageQueueManagement::class, RoleConfigurationBase::MAIN,
87
-                    $currentUser) === ISecurityManager::ALLOWED);
88
-        $this->assign('nav__canFormMgmt', $this->getSecurityManager()
89
-                ->allows(PageRequestFormManagement::class, RoleConfigurationBase::MAIN,
90
-                    $currentUser) === ISecurityManager::ALLOWED);
91
-        $this->assign('nav__canErrorLog', $this->getSecurityManager()
92
-                ->allows(PageErrorLogViewer::class, RoleConfigurationBase::MAIN, $currentUser) === ISecurityManager::ALLOWED);
63
+		$this->assign('nav__canBan', $this->getSecurityManager()
64
+				->allows(PageBan::class, RoleConfigurationBase::MAIN, $currentUser) === ISecurityManager::ALLOWED);
65
+		$this->assign('nav__canEmailMgmt', $this->getSecurityManager()
66
+				->allows(PageEmailManagement::class, RoleConfigurationBase::MAIN,
67
+					$currentUser) === ISecurityManager::ALLOWED);
68
+		$this->assign('nav__canWelcomeMgmt', $this->getSecurityManager()
69
+				->allows(PageWelcomeTemplateManagement::class, RoleConfigurationBase::MAIN,
70
+					$currentUser) === ISecurityManager::ALLOWED);
71
+		$this->assign('nav__canSiteNoticeMgmt', $this->getSecurityManager()
72
+				->allows(PageSiteNotice::class, RoleConfigurationBase::MAIN, $currentUser) === ISecurityManager::ALLOWED);
73
+		$this->assign('nav__canUserMgmt', $this->getSecurityManager()
74
+				->allows(PageUserManagement::class, RoleConfigurationBase::MAIN,
75
+					$currentUser) === ISecurityManager::ALLOWED);
76
+		$this->assign('nav__canJobQueue', $this->getSecurityManager()
77
+				->allows(PageJobQueue::class, RoleConfigurationBase::MAIN,
78
+					$currentUser) === ISecurityManager::ALLOWED);
79
+		$this->assign('nav__canDomainMgmt', $this->getSecurityManager()
80
+				->allows(PageDomainManagement::class, RoleConfigurationBase::MAIN,
81
+					$currentUser) === ISecurityManager::ALLOWED);
82
+		$this->assign('nav__canFlaggedComments', $this->getSecurityManager()
83
+				->allows(PageListFlaggedComments::class, RoleConfigurationBase::MAIN,
84
+					$currentUser) === ISecurityManager::ALLOWED);
85
+		$this->assign('nav__canQueueMgmt', $this->getSecurityManager()
86
+				->allows(PageQueueManagement::class, RoleConfigurationBase::MAIN,
87
+					$currentUser) === ISecurityManager::ALLOWED);
88
+		$this->assign('nav__canFormMgmt', $this->getSecurityManager()
89
+				->allows(PageRequestFormManagement::class, RoleConfigurationBase::MAIN,
90
+					$currentUser) === ISecurityManager::ALLOWED);
91
+		$this->assign('nav__canErrorLog', $this->getSecurityManager()
92
+				->allows(PageErrorLogViewer::class, RoleConfigurationBase::MAIN, $currentUser) === ISecurityManager::ALLOWED);
93 93
 
94
-        $this->assign('nav__canViewRequest', $this->getSecurityManager()
95
-                ->allows(PageViewRequest::class, RoleConfigurationBase::MAIN, $currentUser) === ISecurityManager::ALLOWED);
94
+		$this->assign('nav__canViewRequest', $this->getSecurityManager()
95
+				->allows(PageViewRequest::class, RoleConfigurationBase::MAIN, $currentUser) === ISecurityManager::ALLOWED);
96 96
 
97
-        $this->assign('nav__domainList', []);
98
-        if ($this->getDomainAccessManager() !== null) {
99
-            $this->assign('nav__domainList', $this->getDomainAccessManager()->getAllowedDomains($currentUser));
100
-        }
101
-    }
97
+		$this->assign('nav__domainList', []);
98
+		if ($this->getDomainAccessManager() !== null) {
99
+			$this->assign('nav__domainList', $this->getDomainAccessManager()->getAllowedDomains($currentUser));
100
+		}
101
+	}
102 102
 
103
-    /**
104
-     * Sets up the badges to draw attention to issues on various admin pages.
105
-     *
106
-     * This function checks to see if a user can access the pages, and if so checks the count of problem areas.
107
-     * If problem areas are found, a number greater than 0 will cause the badge to show up.
108
-     *
109
-     * @param User        $currentUser The current user
110
-     * @param PdoDatabase $database    Database instance
111
-     *
112
-     * @return void
113
-     */
114
-    public function setUpNavBarBadges(User $currentUser, PdoDatabase $database) {
115
-        // Set up some variables.
116
-        // A size of 0 causes nothing to show up on the page (checked on navigation-menu.tpl) so leaving it 0 here is fine.
117
-        $countOfFlagged = 0;
118
-        $countOfJobQueue = 0;
103
+	/**
104
+	 * Sets up the badges to draw attention to issues on various admin pages.
105
+	 *
106
+	 * This function checks to see if a user can access the pages, and if so checks the count of problem areas.
107
+	 * If problem areas are found, a number greater than 0 will cause the badge to show up.
108
+	 *
109
+	 * @param User        $currentUser The current user
110
+	 * @param PdoDatabase $database    Database instance
111
+	 *
112
+	 * @return void
113
+	 */
114
+	public function setUpNavBarBadges(User $currentUser, PdoDatabase $database) {
115
+		// Set up some variables.
116
+		// A size of 0 causes nothing to show up on the page (checked on navigation-menu.tpl) so leaving it 0 here is fine.
117
+		$countOfFlagged = 0;
118
+		$countOfJobQueue = 0;
119 119
 
120
-        // Count of flagged comments:
121
-        if($this->barrierTest(RoleConfigurationBase::MAIN, $currentUser, PageListFlaggedComments::class)) {
122
-            // We want all flagged comments that haven't been acknowledged if we can visit the page.
123
-            $countOfFlagged = sizeof(Comment::getFlaggedComments($database, 1)); // FIXME: domains
124
-        }
120
+		// Count of flagged comments:
121
+		if($this->barrierTest(RoleConfigurationBase::MAIN, $currentUser, PageListFlaggedComments::class)) {
122
+			// We want all flagged comments that haven't been acknowledged if we can visit the page.
123
+			$countOfFlagged = sizeof(Comment::getFlaggedComments($database, 1)); // FIXME: domains
124
+		}
125 125
 
126
-        // Count of failed job queue changes:
127
-        if($this->barrierTest(RoleConfigurationBase::MAIN, $currentUser, PageJobQueue::class)) {
128
-            // We want all failed jobs that haven't been acknowledged if we can visit the page.
129
-            JobQueueSearchHelper::get($database, 1) // FIXME: domains
130
-                ->statusIn([JobQueue::STATUS_FAILED])
131
-                ->notAcknowledged()
132
-                ->getRecordCount($countOfJobQueue);
133
-        }
126
+		// Count of failed job queue changes:
127
+		if($this->barrierTest(RoleConfigurationBase::MAIN, $currentUser, PageJobQueue::class)) {
128
+			// We want all failed jobs that haven't been acknowledged if we can visit the page.
129
+			JobQueueSearchHelper::get($database, 1) // FIXME: domains
130
+				->statusIn([JobQueue::STATUS_FAILED])
131
+				->notAcknowledged()
132
+				->getRecordCount($countOfJobQueue);
133
+		}
134 134
 
135
-        // To generate the main badge, add both up.
136
-        // If we add more badges in the future, don't forget to add them here!
137
-        $countOfAll = $countOfFlagged + $countOfJobQueue;
135
+		// To generate the main badge, add both up.
136
+		// If we add more badges in the future, don't forget to add them here!
137
+		$countOfAll = $countOfFlagged + $countOfJobQueue;
138 138
 
139
-        // Set badge variables
140
-        $this->assign("nav__numFlaggedComments", $countOfFlagged);
141
-        $this->assign("nav__numJobQueueFailed", $countOfJobQueue);
142
-        $this->assign("nav__numAdmin", $countOfAll);
143
-    }
139
+		// Set badge variables
140
+		$this->assign("nav__numFlaggedComments", $countOfFlagged);
141
+		$this->assign("nav__numJobQueueFailed", $countOfJobQueue);
142
+		$this->assign("nav__numAdmin", $countOfAll);
143
+	}
144 144
 }
Please login to merge, or discard this patch.
Spacing   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -118,13 +118,13 @@
 block discarded – undo
118 118
         $countOfJobQueue = 0;
119 119
 
120 120
         // Count of flagged comments:
121
-        if($this->barrierTest(RoleConfigurationBase::MAIN, $currentUser, PageListFlaggedComments::class)) {
121
+        if ($this->barrierTest(RoleConfigurationBase::MAIN, $currentUser, PageListFlaggedComments::class)) {
122 122
             // We want all flagged comments that haven't been acknowledged if we can visit the page.
123 123
             $countOfFlagged = sizeof(Comment::getFlaggedComments($database, 1)); // FIXME: domains
124 124
         }
125 125
 
126 126
         // Count of failed job queue changes:
127
-        if($this->barrierTest(RoleConfigurationBase::MAIN, $currentUser, PageJobQueue::class)) {
127
+        if ($this->barrierTest(RoleConfigurationBase::MAIN, $currentUser, PageJobQueue::class)) {
128 128
             // We want all failed jobs that haven't been acknowledged if we can visit the page.
129 129
             JobQueueSearchHelper::get($database, 1) // FIXME: domains
130 130
                 ->statusIn([JobQueue::STATUS_FAILED])
Please login to merge, or discard this patch.
includes/Fragments/LogEntryLookup.php 1 patch
Indentation   +14 added lines, -14 removed lines patch added patch discarded remove patch
@@ -16,20 +16,20 @@
 block discarded – undo
16 16
 
17 17
 trait LogEntryLookup
18 18
 {
19
-    protected function getLogEntry(string $action, User $user, PdoDatabase $database): ?string
20
-    {
21
-        /** @var Log[] $logs */
22
-        $logs = LogSearchHelper::get($database, null)
23
-            ->byAction($action)
24
-            ->byObjectType('User')
25
-            ->byObjectId($user->getId())
26
-            ->limit(1)
27
-            ->fetch();
19
+	protected function getLogEntry(string $action, User $user, PdoDatabase $database): ?string
20
+	{
21
+		/** @var Log[] $logs */
22
+		$logs = LogSearchHelper::get($database, null)
23
+			->byAction($action)
24
+			->byObjectType('User')
25
+			->byObjectId($user->getId())
26
+			->limit(1)
27
+			->fetch();
28 28
 
29
-        if (count($logs) > 0) {
30
-            return $logs[0]->getComment();
31
-        }
29
+		if (count($logs) > 0) {
30
+			return $logs[0]->getComment();
31
+		}
32 32
 
33
-        return null;
34
-    }
33
+		return null;
34
+	}
35 35
 }
36 36
\ No newline at end of file
Please login to merge, or discard this patch.
includes/Tasks/InternalPageBase.php 2 patches
Indentation   +228 added lines, -228 removed lines patch added patch discarded remove patch
@@ -24,232 +24,232 @@
 block discarded – undo
24 24
 
25 25
 abstract class InternalPageBase extends PageBase
26 26
 {
27
-    use NavigationMenuAccessControl;
28
-
29
-    /** @var ITypeAheadHelper */
30
-    private $typeAheadHelper;
31
-    private ISecurityManager $securityManager;
32
-    /** @var IBlacklistHelper */
33
-    private $blacklistHelper;
34
-
35
-    private IDomainAccessManager $domainAccessManager;
36
-
37
-    /**
38
-     * @return ITypeAheadHelper
39
-     */
40
-    public function getTypeAheadHelper()
41
-    {
42
-        return $this->typeAheadHelper;
43
-    }
44
-
45
-    /**
46
-     * @param ITypeAheadHelper $typeAheadHelper
47
-     */
48
-    public function setTypeAheadHelper(ITypeAheadHelper $typeAheadHelper)
49
-    {
50
-        $this->typeAheadHelper = $typeAheadHelper;
51
-    }
52
-
53
-    /**
54
-     * Runs the page code
55
-     *
56
-     * @throws Exception
57
-     * @category Security-Critical
58
-     */
59
-    final public function execute()
60
-    {
61
-        if ($this->getRouteName() === null) {
62
-            throw new Exception("Request is unrouted.");
63
-        }
64
-
65
-        if ($this->getSiteConfiguration() === null) {
66
-            throw new Exception("Page has no configuration!");
67
-        }
68
-
69
-        $this->setupPage();
70
-
71
-        $this->touchUserLastActive();
72
-
73
-        $currentUser = User::getCurrent($this->getDatabase());
74
-
75
-        // Hey, this is also a security barrier, in addition to the below. Separated out for readability.
76
-        if (!$this->isProtectedPage()) {
77
-            // This page is /not/ a protected page, as such we can just run it.
78
-            $this->runPage();
79
-
80
-            return;
81
-        }
82
-
83
-        // Security barrier.
84
-        //
85
-        // This code essentially doesn't care if the user is logged in or not, as the security manager hides all that
86
-        // away for us
87
-        $securityResult = $this->getSecurityManager()->allows(get_called_class(), $this->getRouteName(), $currentUser);
88
-        if ($securityResult === ISecurityManager::ALLOWED) {
89
-            // We're allowed to run the page, so let's run it.
90
-            $this->runPage();
91
-        }
92
-        else {
93
-            $this->handleAccessDenied($securityResult);
94
-
95
-            // Send the headers
96
-            $this->sendResponseHeaders();
97
-        }
98
-    }
99
-
100
-    /**
101
-     * Performs final tasks needed before rendering the page.
102
-     */
103
-    final public function finalisePage()
104
-    {
105
-        parent::finalisePage();
106
-
107
-        $database = $this->getDatabase();
108
-        $currentUser = User::getCurrent($database);
109
-
110
-        // Load in the badges for the navbar
111
-        $this->setUpNavBarBadges($currentUser, $database);
112
-
113
-        if ($this->barrierTest('viewSiteNotice', User::getCurrent($database), 'GlobalInfo')) {
114
-            $siteNotice = SiteNotice::get($this->getDatabase());
115
-            $siteNoticeHash = sha1($siteNotice);
116
-
117
-            if (WebRequest::testSiteNoticeCookieValue($siteNoticeHash)) {
118
-                $this->assign('siteNoticeState', 'd-none');
119
-            }
120
-            else {
121
-                $this->assign('siteNoticeState', 'd-block');
122
-            }
123
-
124
-            $this->assign('siteNoticeText', $siteNotice);
125
-            $this->assign('siteNoticeVersion', $siteNoticeHash);
126
-        }
127
-
128
-        if ($this->barrierTest('viewOnlineUsers', User::getCurrent($database), 'GlobalInfo')) {
129
-            $sql = 'SELECT * FROM user WHERE lastactive > DATE_SUB(CURRENT_TIMESTAMP(), INTERVAL 5 MINUTE);';
130
-            $statement = $database->query($sql);
131
-            $activeUsers = $statement->fetchAll(PDO::FETCH_CLASS, User::class);
132
-            $this->assign('onlineusers', $activeUsers);
133
-        }
134
-
135
-        $this->setupNavMenuAccess($currentUser);
136
-    }
137
-
138
-    /**
139
-     * Configures whether the page respects roles or not. You probably want this to return true.
140
-     *
141
-     * Set to false for public pages. You probably want this to return true.
142
-     *
143
-     * This defaults to true unless you explicitly set it to false. Setting it to false means anybody can do anything
144
-     * on this page, so you probably want this to return true.
145
-     *
146
-     * @return bool
147
-     * @category Security-Critical
148
-     */
149
-    protected function isProtectedPage()
150
-    {
151
-        return true;
152
-    }
153
-
154
-    protected function handleAccessDenied($denyReason)
155
-    {
156
-        $currentUser = User::getCurrent($this->getDatabase());
157
-
158
-        // Not allowed to access this resource.
159
-        // Firstly, let's check if we're even logged in.
160
-        if ($currentUser->isCommunityUser()) {
161
-            // Not logged in, redirect to login page
162
-            WebRequest::setPostLoginRedirect();
163
-            $this->redirect("login");
164
-
165
-            return;
166
-        }
167
-        else {
168
-            // Decide whether this was a rights failure, or an identification failure.
169
-
170
-            if ($denyReason === ISecurityManager::ERROR_NOT_IDENTIFIED) {
171
-                // Not identified
172
-                throw new NotIdentifiedException($this->getSecurityManager(), $this->getDomainAccessManager());
173
-            }
174
-            elseif ($denyReason === ISecurityManager::ERROR_DENIED) {
175
-                // Nope, plain old access denied
176
-                throw new AccessDeniedException($this->getSecurityManager(), $this->getDomainAccessManager());
177
-            }
178
-            else {
179
-                throw new Exception('Unknown response from security manager.');
180
-            }
181
-        }
182
-    }
183
-
184
-    /**
185
-     * Tests the security barrier for a specified action.
186
-     *
187
-     * Don't use within templates
188
-     *
189
-     * @param string      $action
190
-     *
191
-     * @param User        $user
192
-     * @param null|string $pageName
193
-     *
194
-     * @return bool
195
-     * @category Security-Critical
196
-     */
197
-    final public function barrierTest($action, User $user, $pageName = null)
198
-    {
199
-        $page = get_called_class();
200
-        if ($pageName !== null) {
201
-            $page = $pageName;
202
-        }
203
-
204
-        $securityResult = $this->getSecurityManager()->allows($page, $action, $user);
205
-
206
-        return $securityResult === ISecurityManager::ALLOWED;
207
-    }
208
-
209
-    /**
210
-     * Updates the lastactive timestamp
211
-     */
212
-    private function touchUserLastActive()
213
-    {
214
-        if (WebRequest::getSessionUserId() !== null) {
215
-            $query = 'UPDATE user SET lastactive = CURRENT_TIMESTAMP() WHERE id = :id;';
216
-            $this->getDatabase()->prepare($query)->execute(array(":id" => WebRequest::getSessionUserId()));
217
-        }
218
-    }
219
-
220
-    public function getSecurityManager(): ISecurityManager
221
-    {
222
-        return $this->securityManager;
223
-    }
224
-
225
-    public function setSecurityManager(ISecurityManager $securityManager)
226
-    {
227
-        $this->securityManager = $securityManager;
228
-    }
229
-
230
-    /**
231
-     * @return IBlacklistHelper
232
-     */
233
-    public function getBlacklistHelper()
234
-    {
235
-        return $this->blacklistHelper;
236
-    }
237
-
238
-    /**
239
-     * @param IBlacklistHelper $blacklistHelper
240
-     */
241
-    public function setBlacklistHelper(IBlacklistHelper $blacklistHelper)
242
-    {
243
-        $this->blacklistHelper = $blacklistHelper;
244
-    }
245
-
246
-    public function getDomainAccessManager(): IDomainAccessManager
247
-    {
248
-        return $this->domainAccessManager;
249
-    }
250
-
251
-    public function setDomainAccessManager(IDomainAccessManager $domainAccessManager): void
252
-    {
253
-        $this->domainAccessManager = $domainAccessManager;
254
-    }
27
+	use NavigationMenuAccessControl;
28
+
29
+	/** @var ITypeAheadHelper */
30
+	private $typeAheadHelper;
31
+	private ISecurityManager $securityManager;
32
+	/** @var IBlacklistHelper */
33
+	private $blacklistHelper;
34
+
35
+	private IDomainAccessManager $domainAccessManager;
36
+
37
+	/**
38
+	 * @return ITypeAheadHelper
39
+	 */
40
+	public function getTypeAheadHelper()
41
+	{
42
+		return $this->typeAheadHelper;
43
+	}
44
+
45
+	/**
46
+	 * @param ITypeAheadHelper $typeAheadHelper
47
+	 */
48
+	public function setTypeAheadHelper(ITypeAheadHelper $typeAheadHelper)
49
+	{
50
+		$this->typeAheadHelper = $typeAheadHelper;
51
+	}
52
+
53
+	/**
54
+	 * Runs the page code
55
+	 *
56
+	 * @throws Exception
57
+	 * @category Security-Critical
58
+	 */
59
+	final public function execute()
60
+	{
61
+		if ($this->getRouteName() === null) {
62
+			throw new Exception("Request is unrouted.");
63
+		}
64
+
65
+		if ($this->getSiteConfiguration() === null) {
66
+			throw new Exception("Page has no configuration!");
67
+		}
68
+
69
+		$this->setupPage();
70
+
71
+		$this->touchUserLastActive();
72
+
73
+		$currentUser = User::getCurrent($this->getDatabase());
74
+
75
+		// Hey, this is also a security barrier, in addition to the below. Separated out for readability.
76
+		if (!$this->isProtectedPage()) {
77
+			// This page is /not/ a protected page, as such we can just run it.
78
+			$this->runPage();
79
+
80
+			return;
81
+		}
82
+
83
+		// Security barrier.
84
+		//
85
+		// This code essentially doesn't care if the user is logged in or not, as the security manager hides all that
86
+		// away for us
87
+		$securityResult = $this->getSecurityManager()->allows(get_called_class(), $this->getRouteName(), $currentUser);
88
+		if ($securityResult === ISecurityManager::ALLOWED) {
89
+			// We're allowed to run the page, so let's run it.
90
+			$this->runPage();
91
+		}
92
+		else {
93
+			$this->handleAccessDenied($securityResult);
94
+
95
+			// Send the headers
96
+			$this->sendResponseHeaders();
97
+		}
98
+	}
99
+
100
+	/**
101
+	 * Performs final tasks needed before rendering the page.
102
+	 */
103
+	final public function finalisePage()
104
+	{
105
+		parent::finalisePage();
106
+
107
+		$database = $this->getDatabase();
108
+		$currentUser = User::getCurrent($database);
109
+
110
+		// Load in the badges for the navbar
111
+		$this->setUpNavBarBadges($currentUser, $database);
112
+
113
+		if ($this->barrierTest('viewSiteNotice', User::getCurrent($database), 'GlobalInfo')) {
114
+			$siteNotice = SiteNotice::get($this->getDatabase());
115
+			$siteNoticeHash = sha1($siteNotice);
116
+
117
+			if (WebRequest::testSiteNoticeCookieValue($siteNoticeHash)) {
118
+				$this->assign('siteNoticeState', 'd-none');
119
+			}
120
+			else {
121
+				$this->assign('siteNoticeState', 'd-block');
122
+			}
123
+
124
+			$this->assign('siteNoticeText', $siteNotice);
125
+			$this->assign('siteNoticeVersion', $siteNoticeHash);
126
+		}
127
+
128
+		if ($this->barrierTest('viewOnlineUsers', User::getCurrent($database), 'GlobalInfo')) {
129
+			$sql = 'SELECT * FROM user WHERE lastactive > DATE_SUB(CURRENT_TIMESTAMP(), INTERVAL 5 MINUTE);';
130
+			$statement = $database->query($sql);
131
+			$activeUsers = $statement->fetchAll(PDO::FETCH_CLASS, User::class);
132
+			$this->assign('onlineusers', $activeUsers);
133
+		}
134
+
135
+		$this->setupNavMenuAccess($currentUser);
136
+	}
137
+
138
+	/**
139
+	 * Configures whether the page respects roles or not. You probably want this to return true.
140
+	 *
141
+	 * Set to false for public pages. You probably want this to return true.
142
+	 *
143
+	 * This defaults to true unless you explicitly set it to false. Setting it to false means anybody can do anything
144
+	 * on this page, so you probably want this to return true.
145
+	 *
146
+	 * @return bool
147
+	 * @category Security-Critical
148
+	 */
149
+	protected function isProtectedPage()
150
+	{
151
+		return true;
152
+	}
153
+
154
+	protected function handleAccessDenied($denyReason)
155
+	{
156
+		$currentUser = User::getCurrent($this->getDatabase());
157
+
158
+		// Not allowed to access this resource.
159
+		// Firstly, let's check if we're even logged in.
160
+		if ($currentUser->isCommunityUser()) {
161
+			// Not logged in, redirect to login page
162
+			WebRequest::setPostLoginRedirect();
163
+			$this->redirect("login");
164
+
165
+			return;
166
+		}
167
+		else {
168
+			// Decide whether this was a rights failure, or an identification failure.
169
+
170
+			if ($denyReason === ISecurityManager::ERROR_NOT_IDENTIFIED) {
171
+				// Not identified
172
+				throw new NotIdentifiedException($this->getSecurityManager(), $this->getDomainAccessManager());
173
+			}
174
+			elseif ($denyReason === ISecurityManager::ERROR_DENIED) {
175
+				// Nope, plain old access denied
176
+				throw new AccessDeniedException($this->getSecurityManager(), $this->getDomainAccessManager());
177
+			}
178
+			else {
179
+				throw new Exception('Unknown response from security manager.');
180
+			}
181
+		}
182
+	}
183
+
184
+	/**
185
+	 * Tests the security barrier for a specified action.
186
+	 *
187
+	 * Don't use within templates
188
+	 *
189
+	 * @param string      $action
190
+	 *
191
+	 * @param User        $user
192
+	 * @param null|string $pageName
193
+	 *
194
+	 * @return bool
195
+	 * @category Security-Critical
196
+	 */
197
+	final public function barrierTest($action, User $user, $pageName = null)
198
+	{
199
+		$page = get_called_class();
200
+		if ($pageName !== null) {
201
+			$page = $pageName;
202
+		}
203
+
204
+		$securityResult = $this->getSecurityManager()->allows($page, $action, $user);
205
+
206
+		return $securityResult === ISecurityManager::ALLOWED;
207
+	}
208
+
209
+	/**
210
+	 * Updates the lastactive timestamp
211
+	 */
212
+	private function touchUserLastActive()
213
+	{
214
+		if (WebRequest::getSessionUserId() !== null) {
215
+			$query = 'UPDATE user SET lastactive = CURRENT_TIMESTAMP() WHERE id = :id;';
216
+			$this->getDatabase()->prepare($query)->execute(array(":id" => WebRequest::getSessionUserId()));
217
+		}
218
+	}
219
+
220
+	public function getSecurityManager(): ISecurityManager
221
+	{
222
+		return $this->securityManager;
223
+	}
224
+
225
+	public function setSecurityManager(ISecurityManager $securityManager)
226
+	{
227
+		$this->securityManager = $securityManager;
228
+	}
229
+
230
+	/**
231
+	 * @return IBlacklistHelper
232
+	 */
233
+	public function getBlacklistHelper()
234
+	{
235
+		return $this->blacklistHelper;
236
+	}
237
+
238
+	/**
239
+	 * @param IBlacklistHelper $blacklistHelper
240
+	 */
241
+	public function setBlacklistHelper(IBlacklistHelper $blacklistHelper)
242
+	{
243
+		$this->blacklistHelper = $blacklistHelper;
244
+	}
245
+
246
+	public function getDomainAccessManager(): IDomainAccessManager
247
+	{
248
+		return $this->domainAccessManager;
249
+	}
250
+
251
+	public function setDomainAccessManager(IDomainAccessManager $domainAccessManager): void
252
+	{
253
+		$this->domainAccessManager = $domainAccessManager;
254
+	}
255 255
 }
Please login to merge, or discard this patch.
Braces   +5 added lines, -10 removed lines patch added patch discarded remove patch
@@ -88,8 +88,7 @@  discard block
 block discarded – undo
88 88
         if ($securityResult === ISecurityManager::ALLOWED) {
89 89
             // We're allowed to run the page, so let's run it.
90 90
             $this->runPage();
91
-        }
92
-        else {
91
+        } else {
93 92
             $this->handleAccessDenied($securityResult);
94 93
 
95 94
             // Send the headers
@@ -116,8 +115,7 @@  discard block
 block discarded – undo
116 115
 
117 116
             if (WebRequest::testSiteNoticeCookieValue($siteNoticeHash)) {
118 117
                 $this->assign('siteNoticeState', 'd-none');
119
-            }
120
-            else {
118
+            } else {
121 119
                 $this->assign('siteNoticeState', 'd-block');
122 120
             }
123 121
 
@@ -163,19 +161,16 @@  discard block
 block discarded – undo
163 161
             $this->redirect("login");
164 162
 
165 163
             return;
166
-        }
167
-        else {
164
+        } else {
168 165
             // Decide whether this was a rights failure, or an identification failure.
169 166
 
170 167
             if ($denyReason === ISecurityManager::ERROR_NOT_IDENTIFIED) {
171 168
                 // Not identified
172 169
                 throw new NotIdentifiedException($this->getSecurityManager(), $this->getDomainAccessManager());
173
-            }
174
-            elseif ($denyReason === ISecurityManager::ERROR_DENIED) {
170
+            } elseif ($denyReason === ISecurityManager::ERROR_DENIED) {
175 171
                 // Nope, plain old access denied
176 172
                 throw new AccessDeniedException($this->getSecurityManager(), $this->getDomainAccessManager());
177
-            }
178
-            else {
173
+            } else {
179 174
                 throw new Exception('Unknown response from security manager.');
180 175
             }
181 176
         }
Please login to merge, or discard this patch.
includes/Tasks/TextApiPageBase.php 1 patch
Indentation   +16 added lines, -16 removed lines patch added patch discarded remove patch
@@ -13,23 +13,23 @@
 block discarded – undo
13 13
 
14 14
 abstract class TextApiPageBase extends ApiPageBase implements IRoutedTask
15 15
 {
16
-    final protected function main()
17
-    {
18
-        if (headers_sent()) {
19
-            throw new ApiException('Headers have already been sent - this indicates a bug in the application!');
20
-        }
16
+	final protected function main()
17
+	{
18
+		if (headers_sent()) {
19
+			throw new ApiException('Headers have already been sent - this indicates a bug in the application!');
20
+		}
21 21
 
22
-        try {
23
-            $responseData = $this->runApiPage();
24
-        }
25
-        catch (ApiException $ex) {
26
-            $responseData = $ex->getMessage();
27
-        }
22
+		try {
23
+			$responseData = $this->runApiPage();
24
+		}
25
+		catch (ApiException $ex) {
26
+			$responseData = $ex->getMessage();
27
+		}
28 28
 
29
-        header('Content-Type: text/plain');
29
+		header('Content-Type: text/plain');
30 30
 
31
-        ob_end_clean();
32
-        print($responseData);
33
-        ob_start();
34
-    }
31
+		ob_end_clean();
32
+		print($responseData);
33
+		ob_start();
34
+	}
35 35
 }
Please login to merge, or discard this patch.
includes/API/Actions/CountAction.php 1 patch
Indentation   +92 added lines, -92 removed lines patch added patch discarded remove patch
@@ -22,47 +22,47 @@  discard block
 block discarded – undo
22 22
  */
23 23
 class CountAction extends XmlApiPageBase implements IXmlApiAction
24 24
 {
25
-    /**
26
-     * The target user
27
-     * @var User $user
28
-     */
29
-    private $user;
25
+	/**
26
+	 * The target user
27
+	 * @var User $user
28
+	 */
29
+	private $user;
30 30
 
31
-    public function executeApiAction(DOMElement $apiDocument)
32
-    {
33
-        $username = WebRequest::getString('user');
34
-        if ($username === null) {
35
-            throw new ApiException("Please specify a username");
36
-        }
31
+	public function executeApiAction(DOMElement $apiDocument)
32
+	{
33
+		$username = WebRequest::getString('user');
34
+		if ($username === null) {
35
+			throw new ApiException("Please specify a username");
36
+		}
37 37
 
38
-        $userElement = $this->document->createElement("user");
39
-        $userElement->setAttribute("name", $username);
40
-        $apiDocument->appendChild($userElement);
38
+		$userElement = $this->document->createElement("user");
39
+		$userElement->setAttribute("name", $username);
40
+		$apiDocument->appendChild($userElement);
41 41
 
42
-        $user = User::getByUsername($username, $this->getDatabase());
42
+		$user = User::getByUsername($username, $this->getDatabase());
43 43
 
44
-        if ($user === false) {
45
-            $userElement->setAttribute("missing", "true");
44
+		if ($user === false) {
45
+			$userElement->setAttribute("missing", "true");
46 46
 
47
-            return $apiDocument;
48
-        }
47
+			return $apiDocument;
48
+		}
49 49
 
50
-        $this->user = $user;
50
+		$this->user = $user;
51 51
 
52
-        $userElement->setAttribute("level", $this->user->getStatus());
53
-        $userElement->setAttribute("created", $this->getAccountsCreated());
52
+		$userElement->setAttribute("level", $this->user->getStatus());
53
+		$userElement->setAttribute("created", $this->getAccountsCreated());
54 54
 
55
-        $userElement->setAttribute("today", $this->getToday());
55
+		$userElement->setAttribute("today", $this->getToday());
56 56
 
57
-        // Let the IRC bot handle the result of this.
58
-        $this->fetchAdminData($userElement);
57
+		// Let the IRC bot handle the result of this.
58
+		$this->fetchAdminData($userElement);
59 59
 
60
-        return $apiDocument;
61
-    }
60
+		return $apiDocument;
61
+	}
62 62
 
63
-    private function getAccountsCreated()
64
-    {
65
-        $query = <<<QUERY
63
+	private function getAccountsCreated()
64
+	{
65
+		$query = <<<QUERY
66 66
         SELECT COUNT(*) AS count
67 67
         FROM log
68 68
             LEFT JOIN emailtemplate ON concat('Closed ', emailtemplate.id) = log.action
@@ -73,17 +73,17 @@  discard block
 block discarded – undo
73 73
             AND user.username = :username;
74 74
 QUERY;
75 75
 
76
-        $statement = $this->getDatabase()->prepare($query);
77
-        $statement->execute(array(":username" => $this->user->getUsername(), ":created" => EmailTemplate::ACTION_CREATED));
78
-        $result = $statement->fetchColumn();
79
-        $statement->closeCursor();
76
+		$statement = $this->getDatabase()->prepare($query);
77
+		$statement->execute(array(":username" => $this->user->getUsername(), ":created" => EmailTemplate::ACTION_CREATED));
78
+		$result = $statement->fetchColumn();
79
+		$statement->closeCursor();
80 80
 
81
-        return $result;
82
-    }
81
+		return $result;
82
+	}
83 83
 
84
-    private function getToday()
85
-    {
86
-        $query = <<<QUERY
84
+	private function getToday()
85
+	{
86
+		$query = <<<QUERY
87 87
         SELECT
88 88
             COUNT(*) AS count
89 89
         FROM log
@@ -95,75 +95,75 @@  discard block
 block discarded – undo
95 95
             AND user.username = :username;
96 96
 QUERY;
97 97
 
98
-        $statement = $this->getDatabase()->prepare($query);
99
-        $statement->bindValue(":username", $this->user->getUsername());
100
-        $statement->bindValue(":date", date('Y-m-d') . "%");
101
-        $statement->bindValue(":created", EmailTemplate::ACTION_CREATED);
102
-        $statement->execute();
103
-        $today = $statement->fetchColumn();
104
-        $statement->closeCursor();
98
+		$statement = $this->getDatabase()->prepare($query);
99
+		$statement->bindValue(":username", $this->user->getUsername());
100
+		$statement->bindValue(":date", date('Y-m-d') . "%");
101
+		$statement->bindValue(":created", EmailTemplate::ACTION_CREATED);
102
+		$statement->execute();
103
+		$today = $statement->fetchColumn();
104
+		$statement->closeCursor();
105 105
 
106
-        return $today;
107
-    }
106
+		return $today;
107
+	}
108 108
 
109
-    private function fetchAdminData(DOMElement $userElement)
110
-    {
111
-        $query = "SELECT COUNT(*) AS count FROM log WHERE log.user = :userid AND log.action = :action;";
109
+	private function fetchAdminData(DOMElement $userElement)
110
+	{
111
+		$query = "SELECT COUNT(*) AS count FROM log WHERE log.user = :userid AND log.action = :action;";
112 112
 
113
-        $statement = $this->getDatabase()->prepare($query);
114
-        $statement->bindValue(":userid", $this->user->getId());
113
+		$statement = $this->getDatabase()->prepare($query);
114
+		$statement->bindValue(":userid", $this->user->getId());
115 115
         
116
-        // Each entry is in the form [ database string, attribute name ]
117
-        // and it happens to be that the attribute is just the lower case form of the database value
118
-        $actions = [
119
-            ['Promoted', 'promoted'],
120
-            ['Approved', 'approved'],
121
-            ['Demoted', 'demoted'],
122
-            ['Renamed', 'renamed'],
123
-            ['Edited', 'edited'],
124
-            ['Prefchange', 'prefchange'],
125
-            ['DeactivatedUser', 'deactivateduser'],
126
-        ];
127
-        foreach ($actions as $action) {
128
-            $dbValue = $action[0];
129
-            $attributeName = $action[1];
116
+		// Each entry is in the form [ database string, attribute name ]
117
+		// and it happens to be that the attribute is just the lower case form of the database value
118
+		$actions = [
119
+			['Promoted', 'promoted'],
120
+			['Approved', 'approved'],
121
+			['Demoted', 'demoted'],
122
+			['Renamed', 'renamed'],
123
+			['Edited', 'edited'],
124
+			['Prefchange', 'prefchange'],
125
+			['DeactivatedUser', 'deactivateduser'],
126
+		];
127
+		foreach ($actions as $action) {
128
+			$dbValue = $action[0];
129
+			$attributeName = $action[1];
130 130
             
131
-            $statement->bindValue(":action", $dbValue);
132
-            $statement->execute();
133
-            $attributeValue = $statement->fetchColumn();
134
-            $userElement->setAttribute($attributeName, $attributeValue);
135
-            $statement->closeCursor();
136
-        }
137
-
138
-        // Combine all three actions affecting Welcome templates into one count.
139
-        $combinedquery = $this->getDatabase()->prepare(<<<SQL
131
+			$statement->bindValue(":action", $dbValue);
132
+			$statement->execute();
133
+			$attributeValue = $statement->fetchColumn();
134
+			$userElement->setAttribute($attributeName, $attributeValue);
135
+			$statement->closeCursor();
136
+		}
137
+
138
+		// Combine all three actions affecting Welcome templates into one count.
139
+		$combinedquery = $this->getDatabase()->prepare(<<<SQL
140 140
             SELECT
141 141
                 COUNT(*) AS count
142 142
             FROM log
143 143
             WHERE log.user = :userid
144 144
                 AND log.action IN ('CreatedTemplate', 'EditedTemplate', 'DeletedTemplate');
145 145
 SQL
146
-        );
146
+		);
147 147
 
148
-        $combinedquery->bindValue(":userid", $this->user->getId());
149
-        $combinedquery->execute();
150
-        $dtc = $combinedquery->fetchColumn();
151
-        $userElement->setAttribute("welctempchange", $dtc);
152
-        $combinedquery->closeCursor();
148
+		$combinedquery->bindValue(":userid", $this->user->getId());
149
+		$combinedquery->execute();
150
+		$dtc = $combinedquery->fetchColumn();
151
+		$userElement->setAttribute("welctempchange", $dtc);
152
+		$combinedquery->closeCursor();
153 153
 
154
-        // Combine both actions affecting Email templates into one count.
155
-        $combinedquery = $this->getDatabase()->prepare(<<<SQL
154
+		// Combine both actions affecting Email templates into one count.
155
+		$combinedquery = $this->getDatabase()->prepare(<<<SQL
156 156
             SELECT COUNT(*) AS count
157 157
             FROM log
158 158
             WHERE log.user = :userid
159 159
                 AND log.action IN ('CreatedEmail', 'EditedEmail');
160 160
 SQL
161
-        );
162
-
163
-        $combinedquery->bindValue(":userid", $this->user->getId());
164
-        $combinedquery->execute();
165
-        $cec = $combinedquery->fetchColumn();
166
-        $userElement->setAttribute("emailtempchange", $cec);
167
-        $combinedquery->closeCursor();
168
-    }
161
+		);
162
+
163
+		$combinedquery->bindValue(":userid", $this->user->getId());
164
+		$combinedquery->execute();
165
+		$cec = $combinedquery->fetchColumn();
166
+		$userElement->setAttribute("emailtempchange", $cec);
167
+		$combinedquery->closeCursor();
168
+	}
169 169
 }
Please login to merge, or discard this patch.
includes/Security/SecurityManager.php 1 patch
Indentation   +165 added lines, -165 removed lines patch added patch discarded remove patch
@@ -16,169 +16,169 @@
 block discarded – undo
16 16
 
17 17
 final class SecurityManager implements ISecurityManager
18 18
 {
19
-    private IIdentificationVerifier $identificationVerifier;
20
-    private RoleConfigurationBase $roleConfiguration;
21
-
22
-    private array $cache = [];
23
-    private IUserAccessLoader $userAccessLoader;
24
-
25
-    public function __construct(
26
-        IIdentificationVerifier $identificationVerifier,
27
-        RoleConfigurationBase $roleConfiguration,
28
-        IUserAccessLoader $userAccessLoader
29
-    ) {
30
-        $this->identificationVerifier = $identificationVerifier;
31
-        $this->roleConfiguration = $roleConfiguration;
32
-        $this->userAccessLoader = $userAccessLoader;
33
-    }
34
-
35
-    /**
36
-     * Tests if a user is allowed to perform an action.
37
-     *
38
-     * This method should form a hard, deterministic security barrier, and only return true if it is absolutely sure
39
-     * that a user should have access to something.
40
-     *
41
-     * @category Security-Critical
42
-     */
43
-    public function allows(string $page, string $route, User $user): int
44
-    {
45
-        $this->getCachedActiveRoles($user, $activeRoles, $inactiveRoles);
46
-
47
-        $availableRights = $this->roleConfiguration->getResultantRole($activeRoles);
48
-        $testResult = $this->findResult($availableRights, $page, $route);
49
-
50
-        if ($testResult !== null) {
51
-            // We got a firm result here, so just return it.
52
-            return $testResult;
53
-        }
54
-
55
-        // No firm result yet, so continue testing the inactive roles so we can give a better error.
56
-        $inactiveRights = $this->roleConfiguration->getResultantRole($inactiveRoles);
57
-        $testResult = $this->findResult($inactiveRights, $page, $route);
58
-
59
-        if ($testResult === self::ALLOWED) {
60
-            // The user is allowed to access this, but their role is inactive.
61
-            return self::ERROR_NOT_IDENTIFIED;
62
-        }
63
-
64
-        // Other options from the secondary test are denied and inconclusive, which at this point defaults to denied.
65
-        return self::ERROR_DENIED;
66
-    }
67
-
68
-    public function getActiveRoles(User $user, ?array &$activeRoles, ?array &$inactiveRoles)
69
-    {
70
-        // Default to the community user here, because the main user is logged out
71
-        $identified = false;
72
-        $userRoles = array('public');
73
-
74
-        // if we're not the community user, get our real rights.
75
-        if (!$user->isCommunityUser()) {
76
-            // Check the user's status - only active users are allowed the effects of roles
77
-
78
-            $userRoles[] = 'loggedIn';
79
-
80
-            if ($user->isActive()) {
81
-                // All active users get +user
82
-                $userRoles[] = 'user';
83
-
84
-                $loadedRoles = $this->userAccessLoader->loadRolesForUser($user);
85
-
86
-                // NOTE: public is still in this array.
87
-                $userRoles = array_merge($userRoles, $loadedRoles);
88
-
89
-                $identified = $this->userIsIdentified($user);
90
-            }
91
-        }
92
-
93
-        $activeRoles = array();
94
-        $inactiveRoles = array();
95
-
96
-        foreach ($userRoles as $v) {
97
-            if ($this->roleConfiguration->roleNeedsIdentification($v)) {
98
-                if ($identified) {
99
-                    $activeRoles[] = $v;
100
-                }
101
-                else {
102
-                    $inactiveRoles[] = $v;
103
-                }
104
-            }
105
-            else {
106
-                $activeRoles[] = $v;
107
-            }
108
-        }
109
-    }
110
-
111
-    public function getCachedActiveRoles(User $user, ?array &$activeRoles, ?array &$inactiveRoles): void
112
-    {
113
-        if (!array_key_exists($user->getId(), $this->cache)) {
114
-            $this->getActiveRoles($user, $retrievedActiveRoles, $retrievedInactiveRoles);
115
-            $this->cache[$user->getId()] = ['active' => $retrievedActiveRoles, 'inactive' => $retrievedInactiveRoles];
116
-        }
117
-
118
-        $activeRoles = $this->cache[$user->getId()]['active'];
119
-        $inactiveRoles = $this->cache[$user->getId()]['inactive'];
120
-    }
121
-
122
-    public function getAvailableRoles(): array
123
-    {
124
-        return $this->roleConfiguration->getAvailableRoles();
125
-    }
126
-
127
-    /**
128
-     * Tests a role for an ACL decision on a specific page/route
129
-     *
130
-     * @param array  $pseudoRole The role (flattened) to check
131
-     * @param string $page       The page class to check
132
-     * @param string $route      The page route to check
133
-     *
134
-     * @return int|null
135
-     */
136
-    private function findResult($pseudoRole, $page, $route)
137
-    {
138
-        if (isset($pseudoRole[$page])) {
139
-            // check for deny on catch-all route
140
-            if (isset($pseudoRole[$page][RoleConfigurationBase::ALL])) {
141
-                if ($pseudoRole[$page][RoleConfigurationBase::ALL] === RoleConfigurationBase::ACCESS_DENY) {
142
-                    return self::ERROR_DENIED;
143
-                }
144
-            }
145
-
146
-            // check normal route
147
-            if (isset($pseudoRole[$page][$route])) {
148
-                if ($pseudoRole[$page][$route] === RoleConfigurationBase::ACCESS_DENY) {
149
-                    return self::ERROR_DENIED;
150
-                }
151
-
152
-                if ($pseudoRole[$page][$route] === RoleConfigurationBase::ACCESS_ALLOW) {
153
-                    return self::ALLOWED;
154
-                }
155
-            }
156
-
157
-            // check for allowed on catch-all route
158
-            if (isset($pseudoRole[$page][RoleConfigurationBase::ALL])) {
159
-                if ($pseudoRole[$page][RoleConfigurationBase::ALL] === RoleConfigurationBase::ACCESS_ALLOW) {
160
-                    return self::ALLOWED;
161
-                }
162
-            }
163
-        }
164
-
165
-        // return indeterminate result
166
-        return null;
167
-    }
168
-
169
-    private function userIsIdentified(User $user): bool
170
-    {
171
-        if ($user->getForceIdentified() === false) {
172
-            // User forced to be unidentified in the database.
173
-            return false;
174
-        }
175
-
176
-        if ($user->getForceIdentified() === true) {
177
-            // User forced to be identified in the database.
178
-            return true;
179
-        }
180
-
181
-        // User not forced to any particular identified status; consult IdentificationVerifier
182
-        return $this->identificationVerifier->isUserIdentified($user->getOnWikiName());
183
-    }
19
+	private IIdentificationVerifier $identificationVerifier;
20
+	private RoleConfigurationBase $roleConfiguration;
21
+
22
+	private array $cache = [];
23
+	private IUserAccessLoader $userAccessLoader;
24
+
25
+	public function __construct(
26
+		IIdentificationVerifier $identificationVerifier,
27
+		RoleConfigurationBase $roleConfiguration,
28
+		IUserAccessLoader $userAccessLoader
29
+	) {
30
+		$this->identificationVerifier = $identificationVerifier;
31
+		$this->roleConfiguration = $roleConfiguration;
32
+		$this->userAccessLoader = $userAccessLoader;
33
+	}
34
+
35
+	/**
36
+	 * Tests if a user is allowed to perform an action.
37
+	 *
38
+	 * This method should form a hard, deterministic security barrier, and only return true if it is absolutely sure
39
+	 * that a user should have access to something.
40
+	 *
41
+	 * @category Security-Critical
42
+	 */
43
+	public function allows(string $page, string $route, User $user): int
44
+	{
45
+		$this->getCachedActiveRoles($user, $activeRoles, $inactiveRoles);
46
+
47
+		$availableRights = $this->roleConfiguration->getResultantRole($activeRoles);
48
+		$testResult = $this->findResult($availableRights, $page, $route);
49
+
50
+		if ($testResult !== null) {
51
+			// We got a firm result here, so just return it.
52
+			return $testResult;
53
+		}
54
+
55
+		// No firm result yet, so continue testing the inactive roles so we can give a better error.
56
+		$inactiveRights = $this->roleConfiguration->getResultantRole($inactiveRoles);
57
+		$testResult = $this->findResult($inactiveRights, $page, $route);
58
+
59
+		if ($testResult === self::ALLOWED) {
60
+			// The user is allowed to access this, but their role is inactive.
61
+			return self::ERROR_NOT_IDENTIFIED;
62
+		}
63
+
64
+		// Other options from the secondary test are denied and inconclusive, which at this point defaults to denied.
65
+		return self::ERROR_DENIED;
66
+	}
67
+
68
+	public function getActiveRoles(User $user, ?array &$activeRoles, ?array &$inactiveRoles)
69
+	{
70
+		// Default to the community user here, because the main user is logged out
71
+		$identified = false;
72
+		$userRoles = array('public');
73
+
74
+		// if we're not the community user, get our real rights.
75
+		if (!$user->isCommunityUser()) {
76
+			// Check the user's status - only active users are allowed the effects of roles
77
+
78
+			$userRoles[] = 'loggedIn';
79
+
80
+			if ($user->isActive()) {
81
+				// All active users get +user
82
+				$userRoles[] = 'user';
83
+
84
+				$loadedRoles = $this->userAccessLoader->loadRolesForUser($user);
85
+
86
+				// NOTE: public is still in this array.
87
+				$userRoles = array_merge($userRoles, $loadedRoles);
88
+
89
+				$identified = $this->userIsIdentified($user);
90
+			}
91
+		}
92
+
93
+		$activeRoles = array();
94
+		$inactiveRoles = array();
95
+
96
+		foreach ($userRoles as $v) {
97
+			if ($this->roleConfiguration->roleNeedsIdentification($v)) {
98
+				if ($identified) {
99
+					$activeRoles[] = $v;
100
+				}
101
+				else {
102
+					$inactiveRoles[] = $v;
103
+				}
104
+			}
105
+			else {
106
+				$activeRoles[] = $v;
107
+			}
108
+		}
109
+	}
110
+
111
+	public function getCachedActiveRoles(User $user, ?array &$activeRoles, ?array &$inactiveRoles): void
112
+	{
113
+		if (!array_key_exists($user->getId(), $this->cache)) {
114
+			$this->getActiveRoles($user, $retrievedActiveRoles, $retrievedInactiveRoles);
115
+			$this->cache[$user->getId()] = ['active' => $retrievedActiveRoles, 'inactive' => $retrievedInactiveRoles];
116
+		}
117
+
118
+		$activeRoles = $this->cache[$user->getId()]['active'];
119
+		$inactiveRoles = $this->cache[$user->getId()]['inactive'];
120
+	}
121
+
122
+	public function getAvailableRoles(): array
123
+	{
124
+		return $this->roleConfiguration->getAvailableRoles();
125
+	}
126
+
127
+	/**
128
+	 * Tests a role for an ACL decision on a specific page/route
129
+	 *
130
+	 * @param array  $pseudoRole The role (flattened) to check
131
+	 * @param string $page       The page class to check
132
+	 * @param string $route      The page route to check
133
+	 *
134
+	 * @return int|null
135
+	 */
136
+	private function findResult($pseudoRole, $page, $route)
137
+	{
138
+		if (isset($pseudoRole[$page])) {
139
+			// check for deny on catch-all route
140
+			if (isset($pseudoRole[$page][RoleConfigurationBase::ALL])) {
141
+				if ($pseudoRole[$page][RoleConfigurationBase::ALL] === RoleConfigurationBase::ACCESS_DENY) {
142
+					return self::ERROR_DENIED;
143
+				}
144
+			}
145
+
146
+			// check normal route
147
+			if (isset($pseudoRole[$page][$route])) {
148
+				if ($pseudoRole[$page][$route] === RoleConfigurationBase::ACCESS_DENY) {
149
+					return self::ERROR_DENIED;
150
+				}
151
+
152
+				if ($pseudoRole[$page][$route] === RoleConfigurationBase::ACCESS_ALLOW) {
153
+					return self::ALLOWED;
154
+				}
155
+			}
156
+
157
+			// check for allowed on catch-all route
158
+			if (isset($pseudoRole[$page][RoleConfigurationBase::ALL])) {
159
+				if ($pseudoRole[$page][RoleConfigurationBase::ALL] === RoleConfigurationBase::ACCESS_ALLOW) {
160
+					return self::ALLOWED;
161
+				}
162
+			}
163
+		}
164
+
165
+		// return indeterminate result
166
+		return null;
167
+	}
168
+
169
+	private function userIsIdentified(User $user): bool
170
+	{
171
+		if ($user->getForceIdentified() === false) {
172
+			// User forced to be unidentified in the database.
173
+			return false;
174
+		}
175
+
176
+		if ($user->getForceIdentified() === true) {
177
+			// User forced to be identified in the database.
178
+			return true;
179
+		}
180
+
181
+		// User not forced to any particular identified status; consult IdentificationVerifier
182
+		return $this->identificationVerifier->isUserIdentified($user->getOnWikiName());
183
+	}
184 184
 }
Please login to merge, or discard this patch.
includes/Security/ISecurityManager.php 1 patch
Indentation   +21 added lines, -21 removed lines patch added patch discarded remove patch
@@ -12,29 +12,29 @@
 block discarded – undo
12 12
 
13 13
 interface ISecurityManager
14 14
 {
15
-    public const ALLOWED = 1;
16
-    public const ERROR_NOT_IDENTIFIED = 2;
17
-    public const ERROR_DENIED = 3;
15
+	public const ALLOWED = 1;
16
+	public const ERROR_NOT_IDENTIFIED = 2;
17
+	public const ERROR_DENIED = 3;
18 18
 
19
-    /**
20
-     * Tests if a user is allowed to perform an action.
21
-     *
22
-     * This method should form a hard, deterministic security barrier, and only return true if it is absolutely sure
23
-     * that a user should have access to something.
24
-     *
25
-     * @param string $page
26
-     * @param string $route
27
-     * @param User   $user
28
-     *
29
-     * @return int
30
-     *
31
-     * @category Security-Critical
32
-     */
33
-    public function allows(string $page, string $route, User $user): int;
19
+	/**
20
+	 * Tests if a user is allowed to perform an action.
21
+	 *
22
+	 * This method should form a hard, deterministic security barrier, and only return true if it is absolutely sure
23
+	 * that a user should have access to something.
24
+	 *
25
+	 * @param string $page
26
+	 * @param string $route
27
+	 * @param User   $user
28
+	 *
29
+	 * @return int
30
+	 *
31
+	 * @category Security-Critical
32
+	 */
33
+	public function allows(string $page, string $route, User $user): int;
34 34
 
35
-    public function getActiveRoles(User $user, ?array &$activeRoles, ?array &$inactiveRoles);
35
+	public function getActiveRoles(User $user, ?array &$activeRoles, ?array &$inactiveRoles);
36 36
 
37
-    public function getCachedActiveRoles(User $user, ?array &$activeRoles, ?array &$inactiveRoles): void;
37
+	public function getCachedActiveRoles(User $user, ?array &$activeRoles, ?array &$inactiveRoles): void;
38 38
 
39
-    public function getAvailableRoles(): array;
39
+	public function getAvailableRoles(): array;
40 40
 }
41 41
\ No newline at end of file
Please login to merge, or discard this patch.
includes/Security/UserAccessLoader.php 1 patch
Indentation   +21 added lines, -21 removed lines patch added patch discarded remove patch
@@ -15,37 +15,37 @@
 block discarded – undo
15 15
 
16 16
 final class UserAccessLoader implements IUserAccessLoader
17 17
 {
18
-    public function loadRolesForUser(User $user): array
19
-    {
20
-        $domain = Domain::getCurrent($user->getDatabase());
21
-        $userRoles = UserRole::getForUser($user->getId(), $user->getDatabase(), $domain->getId());
18
+	public function loadRolesForUser(User $user): array
19
+	{
20
+		$domain = Domain::getCurrent($user->getDatabase());
21
+		$userRoles = UserRole::getForUser($user->getId(), $user->getDatabase(), $domain->getId());
22 22
 
23
-        return array_map(fn(UserRole $r): string => $r->getRole(), $userRoles);
24
-    }
23
+		return array_map(fn(UserRole $r): string => $r->getRole(), $userRoles);
24
+	}
25 25
 
26
-    public function loadDomainsForUser(User $user): array
27
-    {
28
-        $database = $user->getDatabase();
26
+	public function loadDomainsForUser(User $user): array
27
+	{
28
+		$database = $user->getDatabase();
29 29
 
30
-        $statement = $database->prepare(<<<'SQL'
30
+		$statement = $database->prepare(<<<'SQL'
31 31
             SELECT d.* 
32 32
             FROM domain d
33 33
             INNER JOIN userdomain ud on d.id = ud.domain
34 34
             WHERE ud.user = :user
35 35
             AND d.enabled = 1
36 36
 SQL
37
-        );
38
-        $statement->execute([
39
-            ':user' => $user->getId()
40
-        ]);
37
+		);
38
+		$statement->execute([
39
+			':user' => $user->getId()
40
+		]);
41 41
 
42
-        $resultObjects = $statement->fetchAll(PDO::FETCH_CLASS, Domain::class);
42
+		$resultObjects = $statement->fetchAll(PDO::FETCH_CLASS, Domain::class);
43 43
 
44
-        /** @var Domain $t */
45
-        foreach ($resultObjects as $t) {
46
-            $t->setDatabase($database);
47
-        }
44
+		/** @var Domain $t */
45
+		foreach ($resultObjects as $t) {
46
+			$t->setDatabase($database);
47
+		}
48 48
 
49
-        return $resultObjects;
50
-    }
49
+		return $resultObjects;
50
+	}
51 51
 }
52 52
\ No newline at end of file
Please login to merge, or discard this patch.
includes/Security/IDomainAccessManager.php 1 patch
Indentation   +8 added lines, -8 removed lines patch added patch discarded remove patch
@@ -13,14 +13,14 @@
 block discarded – undo
13 13
 
14 14
 interface IDomainAccessManager
15 15
 {
16
-    public function switchToDefaultDomain(User $user): void;
16
+	public function switchToDefaultDomain(User $user): void;
17 17
 
18
-    public function switchDomain(User $user, Domain $newDomain): void;
18
+	public function switchDomain(User $user, Domain $newDomain): void;
19 19
 
20
-    /**
21
-     * @param User $user
22
-     *
23
-     * @return Domain[]
24
-     */
25
-    public function getAllowedDomains(User $user): array;
20
+	/**
21
+	 * @param User $user
22
+	 *
23
+	 * @return Domain[]
24
+	 */
25
+	public function getAllowedDomains(User $user): array;
26 26
 }
27 27
\ No newline at end of file
Please login to merge, or discard this patch.