Passed
Push — smarty-upgrade-changes ( 30d081...6a268a )
by Michael
03:44
created
includes/Pages/PageLog.php 1 patch
Indentation   +60 added lines, -60 removed lines patch added patch discarded remove patch
@@ -17,64 +17,64 @@
 block discarded – undo
17 17
 
18 18
 class PageLog extends PagedInternalPageBase
19 19
 {
20
-    /**
21
-     * Main function for this page, when no specific actions are called.
22
-     */
23
-    protected function main()
24
-    {
25
-        $this->setHtmlTitle('Logs');
26
-
27
-        $filterUser = WebRequest::getString('filterUser');
28
-        $filterAction = WebRequest::getString('filterAction');
29
-        $filterObjectType = WebRequest::getString('filterObjectType');
30
-        $filterObjectId = WebRequest::getInt('filterObjectId');
31
-
32
-        $database = $this->getDatabase();
33
-
34
-        if (!array_key_exists($filterObjectType, LogHelper::getObjectTypes())) {
35
-            $filterObjectType = null;
36
-        }
37
-
38
-        $this->addJs("/api.php?action=users&all=true&targetVariable=typeaheaddata");
39
-
40
-        // FIXME: domains
41
-        $logSearch = LogSearchHelper::get($database, 1);
42
-
43
-        if ($filterUser !== null) {
44
-            $userObj = User::getByUsername($filterUser, $database);
45
-            if ($userObj !== false) {
46
-                $logSearch->byUser($userObj->getId());
47
-            }
48
-            else {
49
-                $logSearch->byUser(-1);
50
-            }
51
-        }
52
-        if ($filterAction !== null) {
53
-            $logSearch->byAction($filterAction);
54
-        }
55
-        if ($filterObjectType !== null) {
56
-            $logSearch->byObjectType($filterObjectType);
57
-        }
58
-        if ($filterObjectId !== null) {
59
-            $logSearch->byObjectId($filterObjectId);
60
-        }
61
-
62
-        $this->setSearchHelper($logSearch);
63
-        $this->setupLimits();
64
-
65
-        /** @var Log[] $logs */
66
-        $logs = $logSearch->getRecordCount($count)->fetch();
67
-
68
-        list($users, $logData) = LogHelper::prepareLogsForTemplate($logs, $database, $this->getSiteConfiguration());
69
-
70
-        $this->setupPageData($count, array('filterUser' => $filterUser, 'filterAction' => $filterAction, 'filterObjectType' => $filterObjectType, 'filterObjectId' => $filterObjectId));
71
-
72
-        $this->assign("logs", $logData);
73
-        $this->assign("users", $users);
74
-
75
-        $this->assign('allLogActions', LogHelper::getLogActions($this->getDatabase()));
76
-        $this->assign('allObjectTypes', LogHelper::getObjectTypes());
77
-
78
-        $this->setTemplate("logs/main.tpl");
79
-    }
20
+	/**
21
+	 * Main function for this page, when no specific actions are called.
22
+	 */
23
+	protected function main()
24
+	{
25
+		$this->setHtmlTitle('Logs');
26
+
27
+		$filterUser = WebRequest::getString('filterUser');
28
+		$filterAction = WebRequest::getString('filterAction');
29
+		$filterObjectType = WebRequest::getString('filterObjectType');
30
+		$filterObjectId = WebRequest::getInt('filterObjectId');
31
+
32
+		$database = $this->getDatabase();
33
+
34
+		if (!array_key_exists($filterObjectType, LogHelper::getObjectTypes())) {
35
+			$filterObjectType = null;
36
+		}
37
+
38
+		$this->addJs("/api.php?action=users&all=true&targetVariable=typeaheaddata");
39
+
40
+		// FIXME: domains
41
+		$logSearch = LogSearchHelper::get($database, 1);
42
+
43
+		if ($filterUser !== null) {
44
+			$userObj = User::getByUsername($filterUser, $database);
45
+			if ($userObj !== false) {
46
+				$logSearch->byUser($userObj->getId());
47
+			}
48
+			else {
49
+				$logSearch->byUser(-1);
50
+			}
51
+		}
52
+		if ($filterAction !== null) {
53
+			$logSearch->byAction($filterAction);
54
+		}
55
+		if ($filterObjectType !== null) {
56
+			$logSearch->byObjectType($filterObjectType);
57
+		}
58
+		if ($filterObjectId !== null) {
59
+			$logSearch->byObjectId($filterObjectId);
60
+		}
61
+
62
+		$this->setSearchHelper($logSearch);
63
+		$this->setupLimits();
64
+
65
+		/** @var Log[] $logs */
66
+		$logs = $logSearch->getRecordCount($count)->fetch();
67
+
68
+		list($users, $logData) = LogHelper::prepareLogsForTemplate($logs, $database, $this->getSiteConfiguration());
69
+
70
+		$this->setupPageData($count, array('filterUser' => $filterUser, 'filterAction' => $filterAction, 'filterObjectType' => $filterObjectType, 'filterObjectId' => $filterObjectId));
71
+
72
+		$this->assign("logs", $logData);
73
+		$this->assign("users", $users);
74
+
75
+		$this->assign('allLogActions', LogHelper::getLogActions($this->getDatabase()));
76
+		$this->assign('allObjectTypes', LogHelper::getObjectTypes());
77
+
78
+		$this->setTemplate("logs/main.tpl");
79
+	}
80 80
 }
Please login to merge, or discard this patch.
includes/Pages/PageListFlaggedComments.php 1 patch
Indentation   +108 added lines, -108 removed lines patch added patch discarded remove patch
@@ -17,112 +17,112 @@
 block discarded – undo
17 17
 
18 18
 class PageListFlaggedComments extends InternalPageBase
19 19
 {
20
-    /**
21
-     * @inheritDoc
22
-     */
23
-    protected function main()
24
-    {
25
-        $this->setHtmlTitle('Flagged comments');
26
-        $this->setTemplate('flagged-comments.tpl');
27
-
28
-        $database = $this->getDatabase();
29
-        $this->assignCSRFToken();
30
-
31
-        /** @var Comment[] $commentObjects */
32
-        $commentObjects = Comment::getFlaggedComments($database, 1); // FIXME: domains
33
-        $comments = [];
34
-
35
-        $currentUser = User::getCurrent($database);
36
-
37
-        $seeRestrictedComments = $this->barrierTest('seeRestrictedComments', $currentUser, 'RequestData');
38
-        $seeCheckuserComments = $this->barrierTest('seeCheckuserComments', $currentUser, 'RequestData');
39
-        $alwaysSeePrivateData = $this->barrierTest('alwaysSeePrivateData', $currentUser, 'RequestData');
40
-
41
-        foreach ($commentObjects as $object) {
42
-            $data = [
43
-                'visibility'    => $object->getVisibility(),
44
-                'hidden'        => false,
45
-                'hiddenText'    => false,
46
-            ];
47
-
48
-            if (!$alwaysSeePrivateData) {
49
-                // tl;dr: This is a stupid configuration, but let's account for it anyway.
50
-                //
51
-                // Flagged comments are treated as private data. If you don't have the privilege
52
-                // RequestData::alwaysSeePrivateData, then we can't show you the content of the comments here.
53
-                // This page is forced to degrade into basically a list of requests, seriously hampering the usefulness
54
-                // of this page. Still, we need to handle the case where we have access to this page, but not access
55
-                // to private data.
56
-                // At the time of writing, this case does not exist in the current role configuration, but for the role
57
-                // configuration to be free of assumptions, we need this code.
58
-
59
-                /** @var Request $request */
60
-                $request = Request::getById($object->getRequest(), $database);
61
-
62
-                if ($request->getReserved() === $currentUser->getId()) {
63
-                    $data['hiddenText'] = false;
64
-                }
65
-                else {
66
-                    $data['hiddenText'] = true;
67
-                }
68
-            }
69
-
70
-            if ($object->getVisibility() == 'requester' || $object->getVisibility() == 'user') {
71
-                $data['hidden'] = false;
72
-            }
73
-            elseif ($object->getVisibility() == 'admin') {
74
-                if ($seeRestrictedComments) {
75
-                    $data['hidden'] = false;
76
-                }
77
-                else {
78
-                    $data['hidden'] = true;
79
-                }
80
-            }
81
-            elseif ($object->getVisibility() == 'checkuser') {
82
-                if ($seeCheckuserComments) {
83
-                    $data['hidden'] = false;
84
-                }
85
-                else {
86
-                    $data['hidden'] = true;
87
-                }
88
-            }
89
-
90
-            $this->copyCommentData($object, $data, $database);
91
-
92
-            $comments[] = $data;
93
-        }
94
-
95
-        $this->assign('comments', $comments);
96
-        $this->assign('seeRestrictedComments', $seeRestrictedComments);
97
-        $this->assign('seeCheckuserComments', $seeCheckuserComments);
98
-
99
-        $this->assign('editOthersComments', $this->barrierTest('editOthers', $currentUser, PageEditComment::class));
100
-        $this->assign('editComments', $this->barrierTest(RoleConfiguration::MAIN, $currentUser, PageEditComment::class));
101
-        $this->assign('canUnflag', $this->barrierTest('unflag', $currentUser, PageFlagComment::class) && $this->barrierTest(RoleConfiguration::MAIN, $currentUser, PageFlagComment::class));
102
-    }
103
-
104
-    private function copyCommentData(Comment $object, array &$data, PdoDatabase $database): void
105
-    {
106
-        if ($data['hidden']) {
107
-            // All details hidden, so don't copy anything.
108
-            return;
109
-        }
110
-
111
-        /** @var Request $request */
112
-        $request = Request::getById($object->getRequest(), $database);
113
-
114
-        if (!$data['hiddenText']) {
115
-            // Comment text is hidden, but presence of the comment is visible.
116
-            $data['comment'] = $object->getComment();
117
-        }
118
-
119
-        $data['id'] = $object->getId();
120
-        $data['updateversion'] = $object->getUpdateVersion();
121
-        $data['time'] = $object->getTime();
122
-        $data['requestid'] = $object->getRequest();
123
-        $data['request'] = $request->getName();
124
-        $data['requeststatus'] = $request->getStatus();
125
-        $data['userid'] = $object->getUser();
126
-        $data['user'] = User::getById($object->getUser(), $database)->getUsername();
127
-    }
20
+	/**
21
+	 * @inheritDoc
22
+	 */
23
+	protected function main()
24
+	{
25
+		$this->setHtmlTitle('Flagged comments');
26
+		$this->setTemplate('flagged-comments.tpl');
27
+
28
+		$database = $this->getDatabase();
29
+		$this->assignCSRFToken();
30
+
31
+		/** @var Comment[] $commentObjects */
32
+		$commentObjects = Comment::getFlaggedComments($database, 1); // FIXME: domains
33
+		$comments = [];
34
+
35
+		$currentUser = User::getCurrent($database);
36
+
37
+		$seeRestrictedComments = $this->barrierTest('seeRestrictedComments', $currentUser, 'RequestData');
38
+		$seeCheckuserComments = $this->barrierTest('seeCheckuserComments', $currentUser, 'RequestData');
39
+		$alwaysSeePrivateData = $this->barrierTest('alwaysSeePrivateData', $currentUser, 'RequestData');
40
+
41
+		foreach ($commentObjects as $object) {
42
+			$data = [
43
+				'visibility'    => $object->getVisibility(),
44
+				'hidden'        => false,
45
+				'hiddenText'    => false,
46
+			];
47
+
48
+			if (!$alwaysSeePrivateData) {
49
+				// tl;dr: This is a stupid configuration, but let's account for it anyway.
50
+				//
51
+				// Flagged comments are treated as private data. If you don't have the privilege
52
+				// RequestData::alwaysSeePrivateData, then we can't show you the content of the comments here.
53
+				// This page is forced to degrade into basically a list of requests, seriously hampering the usefulness
54
+				// of this page. Still, we need to handle the case where we have access to this page, but not access
55
+				// to private data.
56
+				// At the time of writing, this case does not exist in the current role configuration, but for the role
57
+				// configuration to be free of assumptions, we need this code.
58
+
59
+				/** @var Request $request */
60
+				$request = Request::getById($object->getRequest(), $database);
61
+
62
+				if ($request->getReserved() === $currentUser->getId()) {
63
+					$data['hiddenText'] = false;
64
+				}
65
+				else {
66
+					$data['hiddenText'] = true;
67
+				}
68
+			}
69
+
70
+			if ($object->getVisibility() == 'requester' || $object->getVisibility() == 'user') {
71
+				$data['hidden'] = false;
72
+			}
73
+			elseif ($object->getVisibility() == 'admin') {
74
+				if ($seeRestrictedComments) {
75
+					$data['hidden'] = false;
76
+				}
77
+				else {
78
+					$data['hidden'] = true;
79
+				}
80
+			}
81
+			elseif ($object->getVisibility() == 'checkuser') {
82
+				if ($seeCheckuserComments) {
83
+					$data['hidden'] = false;
84
+				}
85
+				else {
86
+					$data['hidden'] = true;
87
+				}
88
+			}
89
+
90
+			$this->copyCommentData($object, $data, $database);
91
+
92
+			$comments[] = $data;
93
+		}
94
+
95
+		$this->assign('comments', $comments);
96
+		$this->assign('seeRestrictedComments', $seeRestrictedComments);
97
+		$this->assign('seeCheckuserComments', $seeCheckuserComments);
98
+
99
+		$this->assign('editOthersComments', $this->barrierTest('editOthers', $currentUser, PageEditComment::class));
100
+		$this->assign('editComments', $this->barrierTest(RoleConfiguration::MAIN, $currentUser, PageEditComment::class));
101
+		$this->assign('canUnflag', $this->barrierTest('unflag', $currentUser, PageFlagComment::class) && $this->barrierTest(RoleConfiguration::MAIN, $currentUser, PageFlagComment::class));
102
+	}
103
+
104
+	private function copyCommentData(Comment $object, array &$data, PdoDatabase $database): void
105
+	{
106
+		if ($data['hidden']) {
107
+			// All details hidden, so don't copy anything.
108
+			return;
109
+		}
110
+
111
+		/** @var Request $request */
112
+		$request = Request::getById($object->getRequest(), $database);
113
+
114
+		if (!$data['hiddenText']) {
115
+			// Comment text is hidden, but presence of the comment is visible.
116
+			$data['comment'] = $object->getComment();
117
+		}
118
+
119
+		$data['id'] = $object->getId();
120
+		$data['updateversion'] = $object->getUpdateVersion();
121
+		$data['time'] = $object->getTime();
122
+		$data['requestid'] = $object->getRequest();
123
+		$data['request'] = $request->getName();
124
+		$data['requeststatus'] = $request->getStatus();
125
+		$data['userid'] = $object->getUser();
126
+		$data['user'] = User::getById($object->getUser(), $database)->getUsername();
127
+	}
128 128
 }
129 129
\ No newline at end of file
Please login to merge, or discard this patch.
includes/Pages/PageRequestFormManagement.php 1 patch
Indentation   +288 added lines, -288 removed lines patch added patch discarded remove patch
@@ -22,292 +22,292 @@
 block discarded – undo
22 22
 
23 23
 class PageRequestFormManagement extends InternalPageBase
24 24
 {
25
-    protected function main()
26
-    {
27
-        $this->setHtmlTitle('Request Form Management');
28
-
29
-        $database = $this->getDatabase();
30
-        $domainId = Domain::getCurrent($database)->getId();
31
-        $forms = RequestForm::getAllForms($database, $domainId);
32
-        $this->assign('forms', $forms);
33
-
34
-        $queues = [];
35
-        foreach ($forms as $f) {
36
-            $queueId = $f->getOverrideQueue();
37
-            if ($queueId !== null) {
38
-                if (!isset($queues[$queueId])) {
39
-                    /** @var RequestQueue $queue */
40
-                    $queue = RequestQueue::getById($queueId, $this->getDatabase());
41
-
42
-                    if ($queue->getDomain() == $domainId) {
43
-                        $queues[$queueId] = $queue;
44
-                    }
45
-                }
46
-            }
47
-        }
48
-
49
-        $this->assign('queues', $queues);
50
-
51
-        $user = User::getCurrent($database);
52
-        $this->assign('canCreate', $this->barrierTest('create', $user));
53
-        $this->assign('canEdit', $this->barrierTest('edit', $user));
54
-        $this->assign('canView', $this->barrierTest('view', $user));
55
-
56
-        $this->setTemplate('form-management/main.tpl');
57
-    }
58
-
59
-    protected function preview() {
60
-        $previewContent = WebRequest::getSessionContext('preview');
61
-
62
-        $renderer = new MarkdownRenderingHelper();
63
-        $this->assign('renderedContent', $renderer->doRender($previewContent['main']));
64
-        $this->assign('username', $renderer->doRenderInline($previewContent['username']));
65
-        $this->assign('email', $renderer->doRenderInline($previewContent['email']));
66
-        $this->assign('comment', $renderer->doRenderInline($previewContent['comment']));
67
-
68
-        $this->setTemplate('form-management/preview.tpl');
69
-    }
70
-
71
-    protected function create()
72
-    {
73
-        if (WebRequest::wasPosted()) {
74
-            $this->validateCSRFToken();
75
-            $database = $this->getDatabase();
76
-            $domainId = Domain::getCurrent($database)->getId();
77
-
78
-            $form = new RequestForm();
79
-
80
-            $form->setDatabase($database);
81
-            $form->setDomain($domainId);
82
-
83
-            $this->setupObjectFromPost($form);
84
-            $form->setPublicEndpoint(WebRequest::postString('endpoint'));
85
-
86
-            if (WebRequest::postString("preview") === "preview") {
87
-                $this->populateFromObject($form);
88
-
89
-                WebRequest::setSessionContext('preview', [
90
-                    'main' => $form->getFormContent(),
91
-                    'username' => $form->getUsernameHelp(),
92
-                    'email' => $form->getEmailHelp(),
93
-                    'comment' => $form->getCommentHelp(),
94
-                ]);
95
-
96
-                $this->assign('createMode', true);
97
-                $this->setTemplate('form-management/edit.tpl');
98
-
99
-                return;
100
-            }
101
-
102
-            $proceed = true;
103
-
104
-            if (RequestForm::getByPublicEndpoint($database, $form->getPublicEndpoint(), $domainId) !== false) {
105
-                SessionAlert::error("The chosen public endpoint is already in use. Please choose another.");
106
-                $proceed = false;
107
-            }
108
-
109
-            if (preg_match('/^[A-Za-z][a-zA-Z0-9-]*$/', $form->getPublicEndpoint()) !== 1) {
110
-                SessionAlert::error("The chosen public endpoint contains invalid characters");
111
-                $proceed = false;
112
-            }
113
-
114
-            if (RequestForm::getByName($database, $form->getName(), $domainId) !== false) {
115
-                SessionAlert::error("The chosen name is already in use. Please choose another.");
116
-                $proceed = false;
117
-            }
118
-
119
-            if ($form->getOverrideQueue() !== null) {
120
-                /** @var RequestQueue|bool $queue */
121
-                $queue = RequestQueue::getById($form->getOverrideQueue(), $database);
122
-                if ($queue === false || $queue->getDomain() !== $domainId || !$queue->isEnabled()) {
123
-                    SessionAlert::error("The chosen queue does not exist or is disabled.");
124
-                    $proceed = false;
125
-                }
126
-            }
127
-
128
-            if ($proceed) {
129
-                $form->save();
130
-                Logger::requestFormCreated($database, $form);
131
-                $this->redirect('requestFormManagement');
132
-            }
133
-            else {
134
-                $this->populateFromObject($form);
135
-                WebRequest::setSessionContext('preview', [
136
-                    'main' => $form->getFormContent(),
137
-                    'username' => $form->getUsernameHelp(),
138
-                    'email' => $form->getEmailHelp(),
139
-                    'comment' => $form->getCommentHelp(),
140
-                ]);
141
-
142
-                $this->assign('createMode', true);
143
-                $this->setTemplate('form-management/edit.tpl');
144
-            }
145
-        }
146
-        else {
147
-            $this->populateFromObject(new RequestForm());
148
-            WebRequest::setSessionContext('preview', null);
149
-            $this->assign('hidePreview', true);
150
-
151
-            $this->assignCSRFToken();
152
-            $this->assign('createMode', true);
153
-            $this->setTemplate('form-management/edit.tpl');
154
-        }
155
-    }
156
-
157
-    protected function view()
158
-    {
159
-        $database = $this->getDatabase();
160
-
161
-        /** @var RequestForm $form */
162
-        $form = RequestForm::getById(WebRequest::getInt('form'), $database);
163
-
164
-        if ($form->getDomain() !== Domain::getCurrent($database)->getId()) {
165
-            throw new AccessDeniedException($this->getSecurityManager(), $this->getDomainAccessManager());
166
-        }
167
-
168
-        $this->populateFromObject($form);
169
-
170
-        if ($form->getOverrideQueue() !== null) {
171
-            $this->assign('queueObject', RequestQueue::getById($form->getOverrideQueue(), $database));
172
-        }
173
-
174
-        WebRequest::setSessionContext('preview', [
175
-            'main' => $form->getFormContent(),
176
-            'username' => $form->getUsernameHelp(),
177
-            'email' => $form->getEmailHelp(),
178
-            'comment' => $form->getCommentHelp(),
179
-        ]);
180
-
181
-        $renderer = new MarkdownRenderingHelper();
182
-        $this->assign('renderedContent', $renderer->doRender($form->getFormContent()));
183
-
184
-        $this->setTemplate('form-management/view.tpl');
185
-    }
186
-
187
-    protected function edit()
188
-    {
189
-        $database = $this->getDatabase();
190
-
191
-        /** @var RequestForm $form */
192
-        $form = RequestForm::getById(WebRequest::getInt('form'), $database);
193
-
194
-        if ($form->getDomain() !== Domain::getCurrent($database)->getId()) {
195
-            throw new AccessDeniedException($this->getSecurityManager(), $this->getDomainAccessManager());
196
-        }
197
-
198
-        if (WebRequest::wasPosted()) {
199
-            $this->validateCSRFToken();
200
-
201
-            $this->setupObjectFromPost($form);
202
-
203
-            if (WebRequest::postString("preview") === "preview") {
204
-                $this->populateFromObject($form);
205
-
206
-                WebRequest::setSessionContext('preview', [
207
-                    'main' => $form->getFormContent(),
208
-                    'username' => $form->getUsernameHelp(),
209
-                    'email' => $form->getEmailHelp(),
210
-                    'comment' => $form->getCommentHelp(),
211
-                ]);
212
-
213
-                $this->assign('createMode', false);
214
-                $this->setTemplate('form-management/edit.tpl');
215
-
216
-                return;
217
-            }
218
-
219
-            $proceed = true;
220
-
221
-            $foundForm = RequestForm::getByName($database, $form->getName(), $form->getDomain());
222
-            if ($foundForm !== false && $foundForm->getId() !== $form->getId()) {
223
-                SessionAlert::error("The chosen name is already in use. Please choose another.");
224
-                $proceed = false;
225
-            }
226
-
227
-            if ($form->getOverrideQueue() !== null) {
228
-                /** @var RequestQueue $queue */
229
-                $queue = RequestQueue::getById($form->getOverrideQueue(), $database);
230
-                if ($queue === false || $queue->getDomain() !== $form->getDomain() || !$queue->isEnabled()) {
231
-                    SessionAlert::error("The chosen queue does not exist or is disabled.");
232
-                    $proceed = false;
233
-                }
234
-            }
235
-
236
-            if ($proceed) {
237
-                Logger::requestFormEdited($database, $form);
238
-                $form->save();
239
-                $this->redirect('requestFormManagement');
240
-            }
241
-            else {
242
-                $this->populateFromObject($form);
243
-                WebRequest::setSessionContext('preview', [
244
-                    'main' => $form->getFormContent(),
245
-                    'username' => $form->getUsernameHelp(),
246
-                    'email' => $form->getEmailHelp(),
247
-                    'comment' => $form->getCommentHelp(),
248
-                ]);
249
-
250
-                $this->assign('createMode', false);
251
-                $this->setTemplate('form-management/edit.tpl');
252
-            }
253
-        }
254
-        else {
255
-            $this->populateFromObject($form);
256
-            WebRequest::setSessionContext('preview', [
257
-                'main' => $form->getFormContent(),
258
-                'username' => $form->getUsernameHelp(),
259
-                'email' => $form->getEmailHelp(),
260
-                'comment' => $form->getCommentHelp(),
261
-            ]);
262
-
263
-            $this->assign('createMode', false);
264
-            $this->setTemplate('form-management/edit.tpl');
265
-        }
266
-    }
267
-
268
-    /**
269
-     * @param RequestForm $form
270
-     */
271
-    protected function populateFromObject(RequestForm $form): void
272
-    {
273
-        $this->assignCSRFToken();
274
-
275
-        $this->assign('name', $form->getName());
276
-        $this->assign('enabled', $form->isEnabled());
277
-        $this->assign('endpoint', $form->getPublicEndpoint());
278
-        $this->assign('queue', $form->getOverrideQueue());
279
-        $this->assign('content', $form->getFormContent());
280
-        $this->assign('username', $form->getUsernameHelp());
281
-        $this->assign('email', $form->getEmailHelp());
282
-        $this->assign('comment', $form->getCommentHelp());
283
-
284
-        $this->assign('domain', $form->getDomainObject());
285
-
286
-        $this->assign('availableQueues', RequestQueue::getEnabledQueues($this->getDatabase()));
287
-    }
288
-
289
-    /**
290
-     * @param RequestForm $form
291
-     *
292
-     * @return void
293
-     * @throws ApplicationLogicException
294
-     */
295
-    protected function setupObjectFromPost(RequestForm $form): void
296
-    {
297
-        if (WebRequest::postString('content') === null
298
-            || WebRequest::postString('username') === null
299
-            || WebRequest::postString('email') === null
300
-            || WebRequest::postString('comment') === null
301
-        ) {
302
-            throw new ApplicationLogicException("Form content, username help, email help, and comment help are all required fields.");
303
-        }
304
-
305
-        $form->setName(WebRequest::postString('name'));
306
-        $form->setEnabled(WebRequest::postBoolean('enabled'));
307
-        $form->setFormContent(WebRequest::postString('content'));
308
-        $form->setOverrideQueue(WebRequest::postInt('queue'));
309
-        $form->setUsernameHelp(WebRequest::postString('username'));
310
-        $form->setEmailHelp(WebRequest::postString('email'));
311
-        $form->setCommentHelp(WebRequest::postString('comment'));
312
-    }
25
+	protected function main()
26
+	{
27
+		$this->setHtmlTitle('Request Form Management');
28
+
29
+		$database = $this->getDatabase();
30
+		$domainId = Domain::getCurrent($database)->getId();
31
+		$forms = RequestForm::getAllForms($database, $domainId);
32
+		$this->assign('forms', $forms);
33
+
34
+		$queues = [];
35
+		foreach ($forms as $f) {
36
+			$queueId = $f->getOverrideQueue();
37
+			if ($queueId !== null) {
38
+				if (!isset($queues[$queueId])) {
39
+					/** @var RequestQueue $queue */
40
+					$queue = RequestQueue::getById($queueId, $this->getDatabase());
41
+
42
+					if ($queue->getDomain() == $domainId) {
43
+						$queues[$queueId] = $queue;
44
+					}
45
+				}
46
+			}
47
+		}
48
+
49
+		$this->assign('queues', $queues);
50
+
51
+		$user = User::getCurrent($database);
52
+		$this->assign('canCreate', $this->barrierTest('create', $user));
53
+		$this->assign('canEdit', $this->barrierTest('edit', $user));
54
+		$this->assign('canView', $this->barrierTest('view', $user));
55
+
56
+		$this->setTemplate('form-management/main.tpl');
57
+	}
58
+
59
+	protected function preview() {
60
+		$previewContent = WebRequest::getSessionContext('preview');
61
+
62
+		$renderer = new MarkdownRenderingHelper();
63
+		$this->assign('renderedContent', $renderer->doRender($previewContent['main']));
64
+		$this->assign('username', $renderer->doRenderInline($previewContent['username']));
65
+		$this->assign('email', $renderer->doRenderInline($previewContent['email']));
66
+		$this->assign('comment', $renderer->doRenderInline($previewContent['comment']));
67
+
68
+		$this->setTemplate('form-management/preview.tpl');
69
+	}
70
+
71
+	protected function create()
72
+	{
73
+		if (WebRequest::wasPosted()) {
74
+			$this->validateCSRFToken();
75
+			$database = $this->getDatabase();
76
+			$domainId = Domain::getCurrent($database)->getId();
77
+
78
+			$form = new RequestForm();
79
+
80
+			$form->setDatabase($database);
81
+			$form->setDomain($domainId);
82
+
83
+			$this->setupObjectFromPost($form);
84
+			$form->setPublicEndpoint(WebRequest::postString('endpoint'));
85
+
86
+			if (WebRequest::postString("preview") === "preview") {
87
+				$this->populateFromObject($form);
88
+
89
+				WebRequest::setSessionContext('preview', [
90
+					'main' => $form->getFormContent(),
91
+					'username' => $form->getUsernameHelp(),
92
+					'email' => $form->getEmailHelp(),
93
+					'comment' => $form->getCommentHelp(),
94
+				]);
95
+
96
+				$this->assign('createMode', true);
97
+				$this->setTemplate('form-management/edit.tpl');
98
+
99
+				return;
100
+			}
101
+
102
+			$proceed = true;
103
+
104
+			if (RequestForm::getByPublicEndpoint($database, $form->getPublicEndpoint(), $domainId) !== false) {
105
+				SessionAlert::error("The chosen public endpoint is already in use. Please choose another.");
106
+				$proceed = false;
107
+			}
108
+
109
+			if (preg_match('/^[A-Za-z][a-zA-Z0-9-]*$/', $form->getPublicEndpoint()) !== 1) {
110
+				SessionAlert::error("The chosen public endpoint contains invalid characters");
111
+				$proceed = false;
112
+			}
113
+
114
+			if (RequestForm::getByName($database, $form->getName(), $domainId) !== false) {
115
+				SessionAlert::error("The chosen name is already in use. Please choose another.");
116
+				$proceed = false;
117
+			}
118
+
119
+			if ($form->getOverrideQueue() !== null) {
120
+				/** @var RequestQueue|bool $queue */
121
+				$queue = RequestQueue::getById($form->getOverrideQueue(), $database);
122
+				if ($queue === false || $queue->getDomain() !== $domainId || !$queue->isEnabled()) {
123
+					SessionAlert::error("The chosen queue does not exist or is disabled.");
124
+					$proceed = false;
125
+				}
126
+			}
127
+
128
+			if ($proceed) {
129
+				$form->save();
130
+				Logger::requestFormCreated($database, $form);
131
+				$this->redirect('requestFormManagement');
132
+			}
133
+			else {
134
+				$this->populateFromObject($form);
135
+				WebRequest::setSessionContext('preview', [
136
+					'main' => $form->getFormContent(),
137
+					'username' => $form->getUsernameHelp(),
138
+					'email' => $form->getEmailHelp(),
139
+					'comment' => $form->getCommentHelp(),
140
+				]);
141
+
142
+				$this->assign('createMode', true);
143
+				$this->setTemplate('form-management/edit.tpl');
144
+			}
145
+		}
146
+		else {
147
+			$this->populateFromObject(new RequestForm());
148
+			WebRequest::setSessionContext('preview', null);
149
+			$this->assign('hidePreview', true);
150
+
151
+			$this->assignCSRFToken();
152
+			$this->assign('createMode', true);
153
+			$this->setTemplate('form-management/edit.tpl');
154
+		}
155
+	}
156
+
157
+	protected function view()
158
+	{
159
+		$database = $this->getDatabase();
160
+
161
+		/** @var RequestForm $form */
162
+		$form = RequestForm::getById(WebRequest::getInt('form'), $database);
163
+
164
+		if ($form->getDomain() !== Domain::getCurrent($database)->getId()) {
165
+			throw new AccessDeniedException($this->getSecurityManager(), $this->getDomainAccessManager());
166
+		}
167
+
168
+		$this->populateFromObject($form);
169
+
170
+		if ($form->getOverrideQueue() !== null) {
171
+			$this->assign('queueObject', RequestQueue::getById($form->getOverrideQueue(), $database));
172
+		}
173
+
174
+		WebRequest::setSessionContext('preview', [
175
+			'main' => $form->getFormContent(),
176
+			'username' => $form->getUsernameHelp(),
177
+			'email' => $form->getEmailHelp(),
178
+			'comment' => $form->getCommentHelp(),
179
+		]);
180
+
181
+		$renderer = new MarkdownRenderingHelper();
182
+		$this->assign('renderedContent', $renderer->doRender($form->getFormContent()));
183
+
184
+		$this->setTemplate('form-management/view.tpl');
185
+	}
186
+
187
+	protected function edit()
188
+	{
189
+		$database = $this->getDatabase();
190
+
191
+		/** @var RequestForm $form */
192
+		$form = RequestForm::getById(WebRequest::getInt('form'), $database);
193
+
194
+		if ($form->getDomain() !== Domain::getCurrent($database)->getId()) {
195
+			throw new AccessDeniedException($this->getSecurityManager(), $this->getDomainAccessManager());
196
+		}
197
+
198
+		if (WebRequest::wasPosted()) {
199
+			$this->validateCSRFToken();
200
+
201
+			$this->setupObjectFromPost($form);
202
+
203
+			if (WebRequest::postString("preview") === "preview") {
204
+				$this->populateFromObject($form);
205
+
206
+				WebRequest::setSessionContext('preview', [
207
+					'main' => $form->getFormContent(),
208
+					'username' => $form->getUsernameHelp(),
209
+					'email' => $form->getEmailHelp(),
210
+					'comment' => $form->getCommentHelp(),
211
+				]);
212
+
213
+				$this->assign('createMode', false);
214
+				$this->setTemplate('form-management/edit.tpl');
215
+
216
+				return;
217
+			}
218
+
219
+			$proceed = true;
220
+
221
+			$foundForm = RequestForm::getByName($database, $form->getName(), $form->getDomain());
222
+			if ($foundForm !== false && $foundForm->getId() !== $form->getId()) {
223
+				SessionAlert::error("The chosen name is already in use. Please choose another.");
224
+				$proceed = false;
225
+			}
226
+
227
+			if ($form->getOverrideQueue() !== null) {
228
+				/** @var RequestQueue $queue */
229
+				$queue = RequestQueue::getById($form->getOverrideQueue(), $database);
230
+				if ($queue === false || $queue->getDomain() !== $form->getDomain() || !$queue->isEnabled()) {
231
+					SessionAlert::error("The chosen queue does not exist or is disabled.");
232
+					$proceed = false;
233
+				}
234
+			}
235
+
236
+			if ($proceed) {
237
+				Logger::requestFormEdited($database, $form);
238
+				$form->save();
239
+				$this->redirect('requestFormManagement');
240
+			}
241
+			else {
242
+				$this->populateFromObject($form);
243
+				WebRequest::setSessionContext('preview', [
244
+					'main' => $form->getFormContent(),
245
+					'username' => $form->getUsernameHelp(),
246
+					'email' => $form->getEmailHelp(),
247
+					'comment' => $form->getCommentHelp(),
248
+				]);
249
+
250
+				$this->assign('createMode', false);
251
+				$this->setTemplate('form-management/edit.tpl');
252
+			}
253
+		}
254
+		else {
255
+			$this->populateFromObject($form);
256
+			WebRequest::setSessionContext('preview', [
257
+				'main' => $form->getFormContent(),
258
+				'username' => $form->getUsernameHelp(),
259
+				'email' => $form->getEmailHelp(),
260
+				'comment' => $form->getCommentHelp(),
261
+			]);
262
+
263
+			$this->assign('createMode', false);
264
+			$this->setTemplate('form-management/edit.tpl');
265
+		}
266
+	}
267
+
268
+	/**
269
+	 * @param RequestForm $form
270
+	 */
271
+	protected function populateFromObject(RequestForm $form): void
272
+	{
273
+		$this->assignCSRFToken();
274
+
275
+		$this->assign('name', $form->getName());
276
+		$this->assign('enabled', $form->isEnabled());
277
+		$this->assign('endpoint', $form->getPublicEndpoint());
278
+		$this->assign('queue', $form->getOverrideQueue());
279
+		$this->assign('content', $form->getFormContent());
280
+		$this->assign('username', $form->getUsernameHelp());
281
+		$this->assign('email', $form->getEmailHelp());
282
+		$this->assign('comment', $form->getCommentHelp());
283
+
284
+		$this->assign('domain', $form->getDomainObject());
285
+
286
+		$this->assign('availableQueues', RequestQueue::getEnabledQueues($this->getDatabase()));
287
+	}
288
+
289
+	/**
290
+	 * @param RequestForm $form
291
+	 *
292
+	 * @return void
293
+	 * @throws ApplicationLogicException
294
+	 */
295
+	protected function setupObjectFromPost(RequestForm $form): void
296
+	{
297
+		if (WebRequest::postString('content') === null
298
+			|| WebRequest::postString('username') === null
299
+			|| WebRequest::postString('email') === null
300
+			|| WebRequest::postString('comment') === null
301
+		) {
302
+			throw new ApplicationLogicException("Form content, username help, email help, and comment help are all required fields.");
303
+		}
304
+
305
+		$form->setName(WebRequest::postString('name'));
306
+		$form->setEnabled(WebRequest::postBoolean('enabled'));
307
+		$form->setFormContent(WebRequest::postString('content'));
308
+		$form->setOverrideQueue(WebRequest::postInt('queue'));
309
+		$form->setUsernameHelp(WebRequest::postString('username'));
310
+		$form->setEmailHelp(WebRequest::postString('email'));
311
+		$form->setCommentHelp(WebRequest::postString('comment'));
312
+	}
313 313
 }
Please login to merge, or discard this patch.
includes/Pages/UserAuth/PageForgotPassword.php 1 patch
Indentation   +209 added lines, -209 removed lines patch added patch discarded remove patch
@@ -23,213 +23,213 @@
 block discarded – undo
23 23
 
24 24
 class PageForgotPassword extends InternalPageBase
25 25
 {
26
-    /**
27
-     * Main function for this page, when no specific actions are called.
28
-     *
29
-     * This is the forgotten password reset form
30
-     * @category Security-Critical
31
-     */
32
-    protected function main()
33
-    {
34
-        if (WebRequest::wasPosted()) {
35
-            $this->validateCSRFToken();
36
-            $username = WebRequest::postString('username');
37
-            $email = WebRequest::postEmail('email');
38
-            $database = $this->getDatabase();
39
-
40
-            if ($username === null || trim($username) === "" || $email === null || trim($email) === "") {
41
-                throw new ApplicationLogicException("Both username and email address must be specified!");
42
-            }
43
-
44
-            $user = User::getByUsername($username, $database);
45
-            $this->sendResetMail($user, $email);
46
-
47
-            SessionAlert::success('<strong>Your password reset request has been completed.</strong> If the details you have provided match our records, you should receive an email shortly.');
48
-
49
-            $this->redirect('login');
50
-        }
51
-        else {
52
-            $this->assignCSRFToken();
53
-            $this->setTemplate('forgot-password/forgotpw.tpl');
54
-        }
55
-    }
56
-
57
-    /**
58
-     * Sends a reset email if the user is authenticated
59
-     *
60
-     * @param User|boolean $user  The user located from the database, or false. Doesn't really matter, since we do the
61
-     *                            check anyway within this method and silently skip if we don't have a user.
62
-     * @param string       $email The provided email address
63
-     */
64
-    private function sendResetMail($user, $email)
65
-    {
66
-        // If the user isn't found, or the email address is wrong, skip sending the details silently.
67
-        if (!$user instanceof User) {
68
-            return;
69
-        }
70
-
71
-        if (strtolower($user->getEmail()) === strtolower($email)) {
72
-            $clientIp = $this->getXffTrustProvider()
73
-                ->getTrustedClientIp(WebRequest::remoteAddress(), WebRequest::forwardedAddress());
74
-
75
-            $this->cleanExistingTokens($user);
76
-
77
-            $hash = Base32::encodeUpper(openssl_random_pseudo_bytes(30));
78
-
79
-            $encryptionHelper = new EncryptionHelper($this->getSiteConfiguration());
80
-
81
-            $cred = new Credential();
82
-            $cred->setDatabase($this->getDatabase());
83
-            $cred->setFactor(-1);
84
-            $cred->setUserId($user->getId());
85
-            $cred->setType('reset');
86
-            $cred->setData($encryptionHelper->encryptData($hash));
87
-            $cred->setVersion(0);
88
-            $cred->setDisabled(0);
89
-            $cred->setTimeout(new DateTimeImmutable('+ 1 hour'));
90
-            $cred->setPriority(9);
91
-            $cred->save();
92
-
93
-            $this->assign("user", $user);
94
-            $this->assign("hash", $hash);
95
-            $this->assign("remoteAddress", $clientIp);
96
-
97
-            $emailContent = $this->fetchTemplate('forgot-password/reset-mail.tpl');
98
-
99
-            // FIXME: domains!
100
-            /** @var Domain $domain */
101
-            $domain = Domain::getById(1, $this->getDatabase());
102
-            $this->getEmailHelper()->sendMail(
103
-                null, $user->getEmail(), "WP:ACC password reset", $emailContent);
104
-        }
105
-    }
106
-
107
-    /**
108
-     * Entry point for the reset action
109
-     *
110
-     * This is the reset password part of the form.
111
-     * @category Security-Critical
112
-     */
113
-    protected function reset()
114
-    {
115
-        $si = WebRequest::getString('si');
116
-        $id = WebRequest::getString('id');
117
-
118
-        if ($si === null || trim($si) === "" || $id === null || trim($id) === "") {
119
-            throw new ApplicationLogicException("Link not valid, please ensure it has copied correctly");
120
-        }
121
-
122
-        $database = $this->getDatabase();
123
-        $user = $this->getResettingUser($id, $database, $si);
124
-
125
-        // Dual mode
126
-        if (WebRequest::wasPosted()) {
127
-            $this->validateCSRFToken();
128
-            try {
129
-                $this->doReset($user);
130
-                $this->cleanExistingTokens($user);
131
-            }
132
-            catch (ApplicationLogicException $ex) {
133
-                SessionAlert::error($ex->getMessage());
134
-                $this->redirect('forgotPassword', 'reset', array('si' => $si, 'id' => $id));
135
-
136
-                return;
137
-            }
138
-        }
139
-        else {
140
-            $this->assignCSRFToken();
141
-            $this->assign('user', $user);
142
-            $this->setTemplate('forgot-password/forgotpwreset.tpl');
143
-            $this->addJs("/vendor/dropbox/zxcvbn/dist/zxcvbn.js");
144
-        }
145
-    }
146
-
147
-    /**
148
-     * Gets the user resetting their password from the database, or throwing an exception if that is not possible.
149
-     *
150
-     * @param integer     $id       The ID of the user to retrieve
151
-     * @param PdoDatabase $database The database object to use
152
-     * @param string      $si       The reset hash provided
153
-     *
154
-     * @return User
155
-     * @throws ApplicationLogicException
156
-     */
157
-    private function getResettingUser($id, $database, $si)
158
-    {
159
-        $user = User::getById($id, $database);
160
-
161
-        if ($user === false || $user->isCommunityUser()) {
162
-            throw new ApplicationLogicException("Password reset failed. Please try again.");
163
-        }
164
-
165
-        $statement = $database->prepare("SELECT * FROM credential WHERE type = 'reset' AND user = :user;");
166
-        $statement->execute([':user' => $user->getId()]);
167
-
168
-        /** @var Credential $credential */
169
-        $credential = $statement->fetchObject(Credential::class);
170
-
171
-        $statement->closeCursor();
172
-
173
-        if ($credential === false) {
174
-            throw new ApplicationLogicException("Password reset failed. Please try again.");
175
-        }
176
-
177
-        $credential->setDatabase($database);
178
-
179
-        $encryptionHelper = new EncryptionHelper($this->getSiteConfiguration());
180
-        if ($encryptionHelper->decryptData($credential->getData()) != $si) {
181
-            throw new ApplicationLogicException("Password reset failed. Please try again.");
182
-        }
183
-
184
-        if ($credential->getTimeout() < new DateTimeImmutable()) {
185
-            $credential->delete();
186
-            throw new ApplicationLogicException("Password reset token expired. Please try again.");
187
-        }
188
-
189
-        return $user;
190
-    }
191
-
192
-    /**
193
-     * Performs the setting of the new password
194
-     *
195
-     * @param User $user The user to set the password for
196
-     *
197
-     * @throws ApplicationLogicException
198
-     */
199
-    private function doReset(User $user)
200
-    {
201
-        $pw = WebRequest::postString('newpassword');
202
-        $pw2 = WebRequest::postString('newpasswordconfirm');
203
-
204
-        if ($pw !== $pw2) {
205
-            throw new ApplicationLogicException('Passwords do not match!');
206
-        }
207
-
208
-        $passwordCredentialProvider = new PasswordCredentialProvider($user->getDatabase(), $this->getSiteConfiguration());
209
-        $passwordCredentialProvider->setCredential($user, 1, $pw);
210
-
211
-        SessionAlert::success('You may now log in!');
212
-        $this->redirect('login');
213
-    }
214
-
215
-    protected function isProtectedPage()
216
-    {
217
-        return false;
218
-    }
219
-
220
-    /**
221
-     * @param $user
222
-     */
223
-    private function cleanExistingTokens($user): void
224
-    {
225
-        // clean out existing reset tokens
226
-        $statement = $this->getDatabase()->prepare("SELECT * FROM credential WHERE type = 'reset' AND user = :user;");
227
-        $statement->execute([':user' => $user->getId()]);
228
-        $existing = $statement->fetchAll(PdoDatabase::FETCH_CLASS, Credential::class);
229
-
230
-        foreach ($existing as $c) {
231
-            $c->setDatabase($this->getDatabase());
232
-            $c->delete();
233
-        }
234
-    }
26
+	/**
27
+	 * Main function for this page, when no specific actions are called.
28
+	 *
29
+	 * This is the forgotten password reset form
30
+	 * @category Security-Critical
31
+	 */
32
+	protected function main()
33
+	{
34
+		if (WebRequest::wasPosted()) {
35
+			$this->validateCSRFToken();
36
+			$username = WebRequest::postString('username');
37
+			$email = WebRequest::postEmail('email');
38
+			$database = $this->getDatabase();
39
+
40
+			if ($username === null || trim($username) === "" || $email === null || trim($email) === "") {
41
+				throw new ApplicationLogicException("Both username and email address must be specified!");
42
+			}
43
+
44
+			$user = User::getByUsername($username, $database);
45
+			$this->sendResetMail($user, $email);
46
+
47
+			SessionAlert::success('<strong>Your password reset request has been completed.</strong> If the details you have provided match our records, you should receive an email shortly.');
48
+
49
+			$this->redirect('login');
50
+		}
51
+		else {
52
+			$this->assignCSRFToken();
53
+			$this->setTemplate('forgot-password/forgotpw.tpl');
54
+		}
55
+	}
56
+
57
+	/**
58
+	 * Sends a reset email if the user is authenticated
59
+	 *
60
+	 * @param User|boolean $user  The user located from the database, or false. Doesn't really matter, since we do the
61
+	 *                            check anyway within this method and silently skip if we don't have a user.
62
+	 * @param string       $email The provided email address
63
+	 */
64
+	private function sendResetMail($user, $email)
65
+	{
66
+		// If the user isn't found, or the email address is wrong, skip sending the details silently.
67
+		if (!$user instanceof User) {
68
+			return;
69
+		}
70
+
71
+		if (strtolower($user->getEmail()) === strtolower($email)) {
72
+			$clientIp = $this->getXffTrustProvider()
73
+				->getTrustedClientIp(WebRequest::remoteAddress(), WebRequest::forwardedAddress());
74
+
75
+			$this->cleanExistingTokens($user);
76
+
77
+			$hash = Base32::encodeUpper(openssl_random_pseudo_bytes(30));
78
+
79
+			$encryptionHelper = new EncryptionHelper($this->getSiteConfiguration());
80
+
81
+			$cred = new Credential();
82
+			$cred->setDatabase($this->getDatabase());
83
+			$cred->setFactor(-1);
84
+			$cred->setUserId($user->getId());
85
+			$cred->setType('reset');
86
+			$cred->setData($encryptionHelper->encryptData($hash));
87
+			$cred->setVersion(0);
88
+			$cred->setDisabled(0);
89
+			$cred->setTimeout(new DateTimeImmutable('+ 1 hour'));
90
+			$cred->setPriority(9);
91
+			$cred->save();
92
+
93
+			$this->assign("user", $user);
94
+			$this->assign("hash", $hash);
95
+			$this->assign("remoteAddress", $clientIp);
96
+
97
+			$emailContent = $this->fetchTemplate('forgot-password/reset-mail.tpl');
98
+
99
+			// FIXME: domains!
100
+			/** @var Domain $domain */
101
+			$domain = Domain::getById(1, $this->getDatabase());
102
+			$this->getEmailHelper()->sendMail(
103
+				null, $user->getEmail(), "WP:ACC password reset", $emailContent);
104
+		}
105
+	}
106
+
107
+	/**
108
+	 * Entry point for the reset action
109
+	 *
110
+	 * This is the reset password part of the form.
111
+	 * @category Security-Critical
112
+	 */
113
+	protected function reset()
114
+	{
115
+		$si = WebRequest::getString('si');
116
+		$id = WebRequest::getString('id');
117
+
118
+		if ($si === null || trim($si) === "" || $id === null || trim($id) === "") {
119
+			throw new ApplicationLogicException("Link not valid, please ensure it has copied correctly");
120
+		}
121
+
122
+		$database = $this->getDatabase();
123
+		$user = $this->getResettingUser($id, $database, $si);
124
+
125
+		// Dual mode
126
+		if (WebRequest::wasPosted()) {
127
+			$this->validateCSRFToken();
128
+			try {
129
+				$this->doReset($user);
130
+				$this->cleanExistingTokens($user);
131
+			}
132
+			catch (ApplicationLogicException $ex) {
133
+				SessionAlert::error($ex->getMessage());
134
+				$this->redirect('forgotPassword', 'reset', array('si' => $si, 'id' => $id));
135
+
136
+				return;
137
+			}
138
+		}
139
+		else {
140
+			$this->assignCSRFToken();
141
+			$this->assign('user', $user);
142
+			$this->setTemplate('forgot-password/forgotpwreset.tpl');
143
+			$this->addJs("/vendor/dropbox/zxcvbn/dist/zxcvbn.js");
144
+		}
145
+	}
146
+
147
+	/**
148
+	 * Gets the user resetting their password from the database, or throwing an exception if that is not possible.
149
+	 *
150
+	 * @param integer     $id       The ID of the user to retrieve
151
+	 * @param PdoDatabase $database The database object to use
152
+	 * @param string      $si       The reset hash provided
153
+	 *
154
+	 * @return User
155
+	 * @throws ApplicationLogicException
156
+	 */
157
+	private function getResettingUser($id, $database, $si)
158
+	{
159
+		$user = User::getById($id, $database);
160
+
161
+		if ($user === false || $user->isCommunityUser()) {
162
+			throw new ApplicationLogicException("Password reset failed. Please try again.");
163
+		}
164
+
165
+		$statement = $database->prepare("SELECT * FROM credential WHERE type = 'reset' AND user = :user;");
166
+		$statement->execute([':user' => $user->getId()]);
167
+
168
+		/** @var Credential $credential */
169
+		$credential = $statement->fetchObject(Credential::class);
170
+
171
+		$statement->closeCursor();
172
+
173
+		if ($credential === false) {
174
+			throw new ApplicationLogicException("Password reset failed. Please try again.");
175
+		}
176
+
177
+		$credential->setDatabase($database);
178
+
179
+		$encryptionHelper = new EncryptionHelper($this->getSiteConfiguration());
180
+		if ($encryptionHelper->decryptData($credential->getData()) != $si) {
181
+			throw new ApplicationLogicException("Password reset failed. Please try again.");
182
+		}
183
+
184
+		if ($credential->getTimeout() < new DateTimeImmutable()) {
185
+			$credential->delete();
186
+			throw new ApplicationLogicException("Password reset token expired. Please try again.");
187
+		}
188
+
189
+		return $user;
190
+	}
191
+
192
+	/**
193
+	 * Performs the setting of the new password
194
+	 *
195
+	 * @param User $user The user to set the password for
196
+	 *
197
+	 * @throws ApplicationLogicException
198
+	 */
199
+	private function doReset(User $user)
200
+	{
201
+		$pw = WebRequest::postString('newpassword');
202
+		$pw2 = WebRequest::postString('newpasswordconfirm');
203
+
204
+		if ($pw !== $pw2) {
205
+			throw new ApplicationLogicException('Passwords do not match!');
206
+		}
207
+
208
+		$passwordCredentialProvider = new PasswordCredentialProvider($user->getDatabase(), $this->getSiteConfiguration());
209
+		$passwordCredentialProvider->setCredential($user, 1, $pw);
210
+
211
+		SessionAlert::success('You may now log in!');
212
+		$this->redirect('login');
213
+	}
214
+
215
+	protected function isProtectedPage()
216
+	{
217
+		return false;
218
+	}
219
+
220
+	/**
221
+	 * @param $user
222
+	 */
223
+	private function cleanExistingTokens($user): void
224
+	{
225
+		// clean out existing reset tokens
226
+		$statement = $this->getDatabase()->prepare("SELECT * FROM credential WHERE type = 'reset' AND user = :user;");
227
+		$statement->execute([':user' => $user->getId()]);
228
+		$existing = $statement->fetchAll(PdoDatabase::FETCH_CLASS, Credential::class);
229
+
230
+		foreach ($existing as $c) {
231
+			$c->setDatabase($this->getDatabase());
232
+			$c->delete();
233
+		}
234
+	}
235 235
 }
Please login to merge, or discard this patch.
includes/Pages/UserAuth/Login/PagePasswordLogin.php 1 patch
Indentation   +27 added lines, -27 removed lines patch added patch discarded remove patch
@@ -13,31 +13,31 @@
 block discarded – undo
13 13
 
14 14
 class PagePasswordLogin extends LoginCredentialPageBase
15 15
 {
16
-    protected function providerSpecificSetup()
17
-    {
18
-        list($partialId, $partialStage) = WebRequest::getAuthPartialLogin();
19
-
20
-        if ($partialId !== null && $partialStage > 1) {
21
-            $sql = 'SELECT type FROM credential WHERE user = :user AND factor = :stage AND disabled = 0 ORDER BY priority';
22
-            $statement = $this->getDatabase()->prepare($sql);
23
-            $statement->execute(array(':user' => $partialId, ':stage' => $partialStage));
24
-            $nextStage = $statement->fetchColumn();
25
-            $statement->closeCursor();
26
-
27
-            $this->redirect("login/" . $this->nextPageMap[$nextStage]);
28
-            return;
29
-        }
30
-
31
-        $this->setTemplate('login/password.tpl');
32
-    }
33
-
34
-    protected function getProviderCredentials()
35
-    {
36
-        $password = WebRequest::postString("password");
37
-        if ($password === null || $password === "") {
38
-            throw new ApplicationLogicException("No password specified");
39
-        }
40
-
41
-        return $password;
42
-    }
16
+	protected function providerSpecificSetup()
17
+	{
18
+		list($partialId, $partialStage) = WebRequest::getAuthPartialLogin();
19
+
20
+		if ($partialId !== null && $partialStage > 1) {
21
+			$sql = 'SELECT type FROM credential WHERE user = :user AND factor = :stage AND disabled = 0 ORDER BY priority';
22
+			$statement = $this->getDatabase()->prepare($sql);
23
+			$statement->execute(array(':user' => $partialId, ':stage' => $partialStage));
24
+			$nextStage = $statement->fetchColumn();
25
+			$statement->closeCursor();
26
+
27
+			$this->redirect("login/" . $this->nextPageMap[$nextStage]);
28
+			return;
29
+		}
30
+
31
+		$this->setTemplate('login/password.tpl');
32
+	}
33
+
34
+	protected function getProviderCredentials()
35
+	{
36
+		$password = WebRequest::postString("password");
37
+		if ($password === null || $password === "") {
38
+			throw new ApplicationLogicException("No password specified");
39
+		}
40
+
41
+		return $password;
42
+	}
43 43
 }
44 44
\ No newline at end of file
Please login to merge, or discard this patch.
includes/Pages/UserAuth/Login/LoginCredentialPageBase.php 1 patch
Indentation   +306 added lines, -306 removed lines patch added patch discarded remove patch
@@ -21,310 +21,310 @@
 block discarded – undo
21 21
 
22 22
 abstract class LoginCredentialPageBase extends InternalPageBase
23 23
 {
24
-    /** @var User */
25
-    protected $partialUser = null;
26
-    protected $nextPageMap = array(
27
-        'yubikeyotp' => 'otp',
28
-        'totp'       => 'otp',
29
-        'scratch'    => 'otp',
30
-    );
31
-    protected $names = array(
32
-        'yubikeyotp' => 'Yubikey OTP',
33
-        'totp'       => 'TOTP (phone code generator)',
34
-        'scratch'    => 'scratch token',
35
-    );
36
-
37
-    /**
38
-     * Main function for this page, when no specific actions are called.
39
-     * @return void
40
-     */
41
-    protected function main()
42
-    {
43
-        if (!$this->enforceHttps()) {
44
-            return;
45
-        }
46
-
47
-        if (WebRequest::wasPosted()) {
48
-            $this->validateCSRFToken();
49
-
50
-            $database = $this->getDatabase();
51
-            try {
52
-                list($partialId, $partialStage) = WebRequest::getAuthPartialLogin();
53
-
54
-                if ($partialStage === null) {
55
-                    $partialStage = 1;
56
-                }
57
-
58
-                if ($partialId === null) {
59
-                    $username = WebRequest::postString('username');
60
-
61
-                    if ($username === null || trim($username) === '') {
62
-                        throw new ApplicationLogicException('No username specified.');
63
-                    }
64
-
65
-                    $user = User::getByUsername($username, $database);
66
-                }
67
-                else {
68
-                    $user = User::getById($partialId, $database);
69
-                }
70
-
71
-                if ($user === false) {
72
-                    throw new ApplicationLogicException("Authentication failed");
73
-                }
74
-
75
-                $authMan = new AuthenticationManager($database, $this->getSiteConfiguration(),
76
-                    $this->getHttpHelper());
77
-
78
-                $credential = $this->getProviderCredentials();
79
-
80
-                $authResult = $authMan->authenticate($user, $credential, $partialStage);
81
-
82
-                if ($authResult === AuthenticationManager::AUTH_FAIL) {
83
-                    throw new ApplicationLogicException("Authentication failed");
84
-                }
85
-
86
-                if ($authResult === AuthenticationManager::AUTH_REQUIRE_NEXT_STAGE) {
87
-                    $this->processJumpNextStage($user, $partialStage, $database);
88
-
89
-                    return;
90
-                }
91
-
92
-                if ($authResult === AuthenticationManager::AUTH_OK) {
93
-                    $this->processLoginSuccess($user);
94
-
95
-                    return;
96
-                }
97
-            }
98
-            catch (ApplicationLogicException $ex) {
99
-                WebRequest::clearAuthPartialLogin();
100
-
101
-                SessionAlert::error($ex->getMessage());
102
-                $this->redirect('login');
103
-
104
-                return;
105
-            }
106
-        }
107
-        else {
108
-            $this->assign('showSignIn', true);
109
-
110
-            $this->setupPartial();
111
-            $this->assignCSRFToken();
112
-            $this->providerSpecificSetup();
113
-        }
114
-    }
115
-
116
-    protected function isProtectedPage()
117
-    {
118
-        return false;
119
-    }
120
-
121
-    /**
122
-     * Enforces HTTPS on the login form
123
-     *
124
-     * @return bool
125
-     */
126
-    private function enforceHttps()
127
-    {
128
-        if ($this->getSiteConfiguration()->getUseStrictTransportSecurity() !== false) {
129
-            if (WebRequest::isHttps()) {
130
-                // Client can clearly use HTTPS, so let's enforce it for all connections.
131
-                $this->headerQueue[] = "Strict-Transport-Security: max-age=15768000";
132
-            }
133
-            else {
134
-                // This is the login form, not the request form. We need protection here.
135
-                $this->redirectUrl('https://' . WebRequest::serverName() . WebRequest::requestUri());
136
-
137
-                return false;
138
-            }
139
-        }
140
-
141
-        return true;
142
-    }
143
-
144
-    protected abstract function providerSpecificSetup();
145
-
146
-    protected function setupPartial()
147
-    {
148
-        $database = $this->getDatabase();
149
-
150
-        // default stuff
151
-        $this->assign('alternatives', array()); // 'u2f' => array('U2F token'), 'otp' => array('TOTP', 'scratch', 'yubiotp')));
152
-
153
-        // is this stage one?
154
-        list($partialId, $partialStage) = WebRequest::getAuthPartialLogin();
155
-        if ($partialStage === null || $partialId === null) {
156
-            WebRequest::clearAuthPartialLogin();
157
-        }
158
-
159
-        // Check to see if we have a partial login in progress
160
-        $username = null;
161
-        if ($partialId !== null) {
162
-            // Yes, enforce this username
163
-            $this->partialUser = User::getById($partialId, $database);
164
-            $username = $this->partialUser->getUsername();
165
-
166
-            $this->setupAlternates($this->partialUser, $partialStage, $database);
167
-        }
168
-        else {
169
-            // No, see if we've preloaded a username
170
-            $preloadUsername = WebRequest::getString('tplUsername');
171
-            if ($preloadUsername !== null) {
172
-                $username = $preloadUsername;
173
-            }
174
-        }
175
-
176
-        if ($partialStage === null) {
177
-            $partialStage = 1;
178
-        }
179
-
180
-        $this->assign('partialStage', $partialStage);
181
-        $this->assign('username', $username);
182
-    }
183
-
184
-    /**
185
-     * Redirect the user back to wherever they came from after a successful login
186
-     *
187
-     * @param User $user
188
-     */
189
-    protected function goBackWhenceYouCame(User $user)
190
-    {
191
-        // Redirect to wherever the user came from
192
-        $redirectDestination = WebRequest::clearPostLoginRedirect();
193
-        if ($redirectDestination !== null) {
194
-            $this->redirectUrl($redirectDestination);
195
-        }
196
-        else {
197
-            if ($user->isNewUser()) {
198
-                // home page isn't allowed, go to preferences instead
199
-                $this->redirect('preferences');
200
-            }
201
-            else {
202
-                // go to the home page
203
-                $this->redirect('');
204
-            }
205
-        }
206
-    }
207
-
208
-    private function processLoginSuccess(User $user)
209
-    {
210
-        // Touch force logout
211
-        $user->setForceLogout(false);
212
-        $user->save();
213
-
214
-        $oauth = new OAuthUserHelper($user, $this->getDatabase(), $this->getOAuthProtocolHelper(),
215
-            $this->getSiteConfiguration());
216
-
217
-        if ($oauth->isFullyLinked()) {
218
-            try {
219
-                // Reload the user's identity ticket.
220
-                $oauth->refreshIdentity();
221
-
222
-                // Check for blocks
223
-                if ($oauth->getIdentity()->getBlocked()) {
224
-                    // blocked!
225
-                    SessionAlert::error("You are currently blocked on-wiki. You will not be able to log in until you are unblocked.");
226
-                    $this->redirect('login');
227
-
228
-                    return;
229
-                }
230
-            }
231
-            catch (OAuthException $ex) {
232
-                // Oops. Refreshing ticket failed. Force a re-auth.
233
-                $authoriseUrl = $oauth->getRequestToken();
234
-                WebRequest::setOAuthPartialLogin($user);
235
-                $this->redirectUrl($authoriseUrl);
236
-
237
-                return;
238
-            }
239
-        }
240
-
241
-        if (($this->getSiteConfiguration()->getEnforceOAuth() && !$oauth->isFullyLinked())
242
-            || $oauth->isPartiallyLinked()
243
-        ) {
244
-            $authoriseUrl = $oauth->getRequestToken();
245
-            WebRequest::setOAuthPartialLogin($user);
246
-            $this->redirectUrl($authoriseUrl);
247
-
248
-            return;
249
-        }
250
-
251
-        WebRequest::setLoggedInUser($user);
252
-        $this->getDomainAccessManager()->switchToDefaultDomain($user);
253
-
254
-        $this->goBackWhenceYouCame($user);
255
-    }
256
-
257
-    protected abstract function getProviderCredentials();
258
-
259
-    /**
260
-     * @param User        $user
261
-     * @param int         $partialStage
262
-     * @param PdoDatabase $database
263
-     *
264
-     * @throws ApplicationLogicException
265
-     */
266
-    private function processJumpNextStage(User $user, $partialStage, PdoDatabase $database)
267
-    {
268
-        WebRequest::setAuthPartialLogin($user->getId(), $partialStage + 1);
269
-
270
-        $sql = 'SELECT type FROM credential WHERE user = :user AND factor = :stage AND disabled = 0 ORDER BY priority';
271
-        $statement = $database->prepare($sql);
272
-        $statement->execute(array(':user' => $user->getId(), ':stage' => $partialStage + 1));
273
-        $nextStage = $statement->fetchColumn();
274
-        $statement->closeCursor();
275
-
276
-        if (!isset($this->nextPageMap[$nextStage])) {
277
-            throw new ApplicationLogicException('Unknown page handler for next authentication stage.');
278
-        }
279
-
280
-        $this->redirect("login/" . $this->nextPageMap[$nextStage]);
281
-    }
282
-
283
-    private function setupAlternates(User $user, $partialStage, PdoDatabase $database)
284
-    {
285
-        // get the providers available
286
-        $sql = 'SELECT type FROM credential WHERE user = :user AND factor = :stage AND disabled = 0';
287
-        $statement = $database->prepare($sql);
288
-        $statement->execute(array(':user' => $user->getId(), ':stage' => $partialStage));
289
-        $alternates = $statement->fetchAll(PDO::FETCH_COLUMN);
290
-
291
-        $types = array();
292
-        foreach ($alternates as $item) {
293
-            $type = $this->nextPageMap[$item];
294
-            if (!isset($types[$type])) {
295
-                $types[$type] = array();
296
-            }
297
-
298
-            $types[$type][] = $item;
299
-        }
300
-
301
-        $userOptions = array();
302
-        if (get_called_class() !== PageOtpLogin::class) {
303
-            $userOptions = array_merge($userOptions, $this->setupUserOptionsForType($types, 'otp', $userOptions));
304
-        }
305
-
306
-        $this->assign('alternatives', $userOptions);
307
-    }
308
-
309
-    /**
310
-     * @param $types
311
-     * @param $type
312
-     * @param $userOptions
313
-     *
314
-     * @return mixed
315
-     */
316
-    private function setupUserOptionsForType($types, $type, $userOptions)
317
-    {
318
-        if (isset($types[$type])) {
319
-            $options = $types[$type];
320
-
321
-            array_walk($options, function(&$val) {
322
-                $val = $this->names[$val];
323
-            });
324
-
325
-            $userOptions[$type] = $options;
326
-        }
327
-
328
-        return $userOptions;
329
-    }
24
+	/** @var User */
25
+	protected $partialUser = null;
26
+	protected $nextPageMap = array(
27
+		'yubikeyotp' => 'otp',
28
+		'totp'       => 'otp',
29
+		'scratch'    => 'otp',
30
+	);
31
+	protected $names = array(
32
+		'yubikeyotp' => 'Yubikey OTP',
33
+		'totp'       => 'TOTP (phone code generator)',
34
+		'scratch'    => 'scratch token',
35
+	);
36
+
37
+	/**
38
+	 * Main function for this page, when no specific actions are called.
39
+	 * @return void
40
+	 */
41
+	protected function main()
42
+	{
43
+		if (!$this->enforceHttps()) {
44
+			return;
45
+		}
46
+
47
+		if (WebRequest::wasPosted()) {
48
+			$this->validateCSRFToken();
49
+
50
+			$database = $this->getDatabase();
51
+			try {
52
+				list($partialId, $partialStage) = WebRequest::getAuthPartialLogin();
53
+
54
+				if ($partialStage === null) {
55
+					$partialStage = 1;
56
+				}
57
+
58
+				if ($partialId === null) {
59
+					$username = WebRequest::postString('username');
60
+
61
+					if ($username === null || trim($username) === '') {
62
+						throw new ApplicationLogicException('No username specified.');
63
+					}
64
+
65
+					$user = User::getByUsername($username, $database);
66
+				}
67
+				else {
68
+					$user = User::getById($partialId, $database);
69
+				}
70
+
71
+				if ($user === false) {
72
+					throw new ApplicationLogicException("Authentication failed");
73
+				}
74
+
75
+				$authMan = new AuthenticationManager($database, $this->getSiteConfiguration(),
76
+					$this->getHttpHelper());
77
+
78
+				$credential = $this->getProviderCredentials();
79
+
80
+				$authResult = $authMan->authenticate($user, $credential, $partialStage);
81
+
82
+				if ($authResult === AuthenticationManager::AUTH_FAIL) {
83
+					throw new ApplicationLogicException("Authentication failed");
84
+				}
85
+
86
+				if ($authResult === AuthenticationManager::AUTH_REQUIRE_NEXT_STAGE) {
87
+					$this->processJumpNextStage($user, $partialStage, $database);
88
+
89
+					return;
90
+				}
91
+
92
+				if ($authResult === AuthenticationManager::AUTH_OK) {
93
+					$this->processLoginSuccess($user);
94
+
95
+					return;
96
+				}
97
+			}
98
+			catch (ApplicationLogicException $ex) {
99
+				WebRequest::clearAuthPartialLogin();
100
+
101
+				SessionAlert::error($ex->getMessage());
102
+				$this->redirect('login');
103
+
104
+				return;
105
+			}
106
+		}
107
+		else {
108
+			$this->assign('showSignIn', true);
109
+
110
+			$this->setupPartial();
111
+			$this->assignCSRFToken();
112
+			$this->providerSpecificSetup();
113
+		}
114
+	}
115
+
116
+	protected function isProtectedPage()
117
+	{
118
+		return false;
119
+	}
120
+
121
+	/**
122
+	 * Enforces HTTPS on the login form
123
+	 *
124
+	 * @return bool
125
+	 */
126
+	private function enforceHttps()
127
+	{
128
+		if ($this->getSiteConfiguration()->getUseStrictTransportSecurity() !== false) {
129
+			if (WebRequest::isHttps()) {
130
+				// Client can clearly use HTTPS, so let's enforce it for all connections.
131
+				$this->headerQueue[] = "Strict-Transport-Security: max-age=15768000";
132
+			}
133
+			else {
134
+				// This is the login form, not the request form. We need protection here.
135
+				$this->redirectUrl('https://' . WebRequest::serverName() . WebRequest::requestUri());
136
+
137
+				return false;
138
+			}
139
+		}
140
+
141
+		return true;
142
+	}
143
+
144
+	protected abstract function providerSpecificSetup();
145
+
146
+	protected function setupPartial()
147
+	{
148
+		$database = $this->getDatabase();
149
+
150
+		// default stuff
151
+		$this->assign('alternatives', array()); // 'u2f' => array('U2F token'), 'otp' => array('TOTP', 'scratch', 'yubiotp')));
152
+
153
+		// is this stage one?
154
+		list($partialId, $partialStage) = WebRequest::getAuthPartialLogin();
155
+		if ($partialStage === null || $partialId === null) {
156
+			WebRequest::clearAuthPartialLogin();
157
+		}
158
+
159
+		// Check to see if we have a partial login in progress
160
+		$username = null;
161
+		if ($partialId !== null) {
162
+			// Yes, enforce this username
163
+			$this->partialUser = User::getById($partialId, $database);
164
+			$username = $this->partialUser->getUsername();
165
+
166
+			$this->setupAlternates($this->partialUser, $partialStage, $database);
167
+		}
168
+		else {
169
+			// No, see if we've preloaded a username
170
+			$preloadUsername = WebRequest::getString('tplUsername');
171
+			if ($preloadUsername !== null) {
172
+				$username = $preloadUsername;
173
+			}
174
+		}
175
+
176
+		if ($partialStage === null) {
177
+			$partialStage = 1;
178
+		}
179
+
180
+		$this->assign('partialStage', $partialStage);
181
+		$this->assign('username', $username);
182
+	}
183
+
184
+	/**
185
+	 * Redirect the user back to wherever they came from after a successful login
186
+	 *
187
+	 * @param User $user
188
+	 */
189
+	protected function goBackWhenceYouCame(User $user)
190
+	{
191
+		// Redirect to wherever the user came from
192
+		$redirectDestination = WebRequest::clearPostLoginRedirect();
193
+		if ($redirectDestination !== null) {
194
+			$this->redirectUrl($redirectDestination);
195
+		}
196
+		else {
197
+			if ($user->isNewUser()) {
198
+				// home page isn't allowed, go to preferences instead
199
+				$this->redirect('preferences');
200
+			}
201
+			else {
202
+				// go to the home page
203
+				$this->redirect('');
204
+			}
205
+		}
206
+	}
207
+
208
+	private function processLoginSuccess(User $user)
209
+	{
210
+		// Touch force logout
211
+		$user->setForceLogout(false);
212
+		$user->save();
213
+
214
+		$oauth = new OAuthUserHelper($user, $this->getDatabase(), $this->getOAuthProtocolHelper(),
215
+			$this->getSiteConfiguration());
216
+
217
+		if ($oauth->isFullyLinked()) {
218
+			try {
219
+				// Reload the user's identity ticket.
220
+				$oauth->refreshIdentity();
221
+
222
+				// Check for blocks
223
+				if ($oauth->getIdentity()->getBlocked()) {
224
+					// blocked!
225
+					SessionAlert::error("You are currently blocked on-wiki. You will not be able to log in until you are unblocked.");
226
+					$this->redirect('login');
227
+
228
+					return;
229
+				}
230
+			}
231
+			catch (OAuthException $ex) {
232
+				// Oops. Refreshing ticket failed. Force a re-auth.
233
+				$authoriseUrl = $oauth->getRequestToken();
234
+				WebRequest::setOAuthPartialLogin($user);
235
+				$this->redirectUrl($authoriseUrl);
236
+
237
+				return;
238
+			}
239
+		}
240
+
241
+		if (($this->getSiteConfiguration()->getEnforceOAuth() && !$oauth->isFullyLinked())
242
+			|| $oauth->isPartiallyLinked()
243
+		) {
244
+			$authoriseUrl = $oauth->getRequestToken();
245
+			WebRequest::setOAuthPartialLogin($user);
246
+			$this->redirectUrl($authoriseUrl);
247
+
248
+			return;
249
+		}
250
+
251
+		WebRequest::setLoggedInUser($user);
252
+		$this->getDomainAccessManager()->switchToDefaultDomain($user);
253
+
254
+		$this->goBackWhenceYouCame($user);
255
+	}
256
+
257
+	protected abstract function getProviderCredentials();
258
+
259
+	/**
260
+	 * @param User        $user
261
+	 * @param int         $partialStage
262
+	 * @param PdoDatabase $database
263
+	 *
264
+	 * @throws ApplicationLogicException
265
+	 */
266
+	private function processJumpNextStage(User $user, $partialStage, PdoDatabase $database)
267
+	{
268
+		WebRequest::setAuthPartialLogin($user->getId(), $partialStage + 1);
269
+
270
+		$sql = 'SELECT type FROM credential WHERE user = :user AND factor = :stage AND disabled = 0 ORDER BY priority';
271
+		$statement = $database->prepare($sql);
272
+		$statement->execute(array(':user' => $user->getId(), ':stage' => $partialStage + 1));
273
+		$nextStage = $statement->fetchColumn();
274
+		$statement->closeCursor();
275
+
276
+		if (!isset($this->nextPageMap[$nextStage])) {
277
+			throw new ApplicationLogicException('Unknown page handler for next authentication stage.');
278
+		}
279
+
280
+		$this->redirect("login/" . $this->nextPageMap[$nextStage]);
281
+	}
282
+
283
+	private function setupAlternates(User $user, $partialStage, PdoDatabase $database)
284
+	{
285
+		// get the providers available
286
+		$sql = 'SELECT type FROM credential WHERE user = :user AND factor = :stage AND disabled = 0';
287
+		$statement = $database->prepare($sql);
288
+		$statement->execute(array(':user' => $user->getId(), ':stage' => $partialStage));
289
+		$alternates = $statement->fetchAll(PDO::FETCH_COLUMN);
290
+
291
+		$types = array();
292
+		foreach ($alternates as $item) {
293
+			$type = $this->nextPageMap[$item];
294
+			if (!isset($types[$type])) {
295
+				$types[$type] = array();
296
+			}
297
+
298
+			$types[$type][] = $item;
299
+		}
300
+
301
+		$userOptions = array();
302
+		if (get_called_class() !== PageOtpLogin::class) {
303
+			$userOptions = array_merge($userOptions, $this->setupUserOptionsForType($types, 'otp', $userOptions));
304
+		}
305
+
306
+		$this->assign('alternatives', $userOptions);
307
+	}
308
+
309
+	/**
310
+	 * @param $types
311
+	 * @param $type
312
+	 * @param $userOptions
313
+	 *
314
+	 * @return mixed
315
+	 */
316
+	private function setupUserOptionsForType($types, $type, $userOptions)
317
+	{
318
+		if (isset($types[$type])) {
319
+			$options = $types[$type];
320
+
321
+			array_walk($options, function(&$val) {
322
+				$val = $this->names[$val];
323
+			});
324
+
325
+			$userOptions[$type] = $options;
326
+		}
327
+
328
+		return $userOptions;
329
+	}
330 330
 }
Please login to merge, or discard this patch.
includes/Pages/UserAuth/PagePreferences.php 1 patch
Indentation   +161 added lines, -161 removed lines patch added patch discarded remove patch
@@ -20,165 +20,165 @@
 block discarded – undo
20 20
 
21 21
 class PagePreferences extends InternalPageBase
22 22
 {
23
-    /**
24
-     * Main function for this page, when no specific actions are called.
25
-     * @return void
26
-     */
27
-    protected function main()
28
-    {
29
-        $this->setHtmlTitle('Preferences');
30
-
31
-        $enforceOAuth = $this->getSiteConfiguration()->getEnforceOAuth();
32
-        $database = $this->getDatabase();
33
-        $user = User::getCurrent($database);
34
-        $preferencesManager = PreferenceManager::getForCurrent($database);
35
-
36
-        // Dual mode
37
-        if (WebRequest::wasPosted()) {
38
-            $this->validateCSRFToken();
39
-
40
-            $this->setPreference($preferencesManager,PreferenceManager::PREF_EMAIL_SIGNATURE, 'emailSignature');
41
-            $this->setPreferenceWithValue($preferencesManager,PreferenceManager::PREF_SKIP_JS_ABORT, 'skipJsAbort', WebRequest::postBoolean('skipJsAbort') ? 1 : 0);
42
-            $this->setPreferenceWithValue($preferencesManager,PreferenceManager::PREF_QUEUE_HELP, 'showQueueHelp', WebRequest::postBoolean('showQueueHelp') ? 1 : 0);
43
-            $this->setCreationMode($user, $preferencesManager);
44
-            $this->setSkin($preferencesManager);
45
-            $preferencesManager->setGlobalPreference(PreferenceManager::PREF_DEFAULT_DOMAIN, WebRequest::postInt('defaultDomain'));
46
-
47
-            $email = WebRequest::postEmail('email');
48
-            if ($email !== null) {
49
-                $user->setEmail($email);
50
-            }
51
-
52
-            $user->save();
53
-            SessionAlert::success("Preferences updated!");
54
-
55
-            if ($this->barrierTest(RoleConfiguration::MAIN, $user, PageMain::class)) {
56
-                $this->redirect('');
57
-            }
58
-            else {
59
-                $this->redirect('preferences');
60
-            }
61
-        }
62
-        else {
63
-            $this->assignCSRFToken();
64
-            $this->setTemplate('preferences/prefs.tpl');
65
-
66
-            // FIXME: domains!
67
-            /** @var Domain $domain */
68
-            $domain = Domain::getById(1, $this->getDatabase());
69
-            $this->assign('mediawikiScriptPath', $domain->getWikiArticlePath());
70
-
71
-            $this->assign("enforceOAuth", $enforceOAuth);
72
-
73
-            $this->assignPreference($preferencesManager, PreferenceManager::PREF_EMAIL_SIGNATURE, 'emailSignature', false);
74
-            $this->assignPreference($preferencesManager, PreferenceManager::PREF_CREATION_MODE, 'creationMode', false);
75
-            $this->assignPreference($preferencesManager, PreferenceManager::PREF_SKIN, 'skin', true);
76
-            $this->assignPreference($preferencesManager, PreferenceManager::PREF_SKIP_JS_ABORT, 'skipJsAbort', false);
77
-            $this->assignPreference($preferencesManager, PreferenceManager::PREF_QUEUE_HELP, 'showQueueHelp', false, true);
78
-            $this->assignPreference($preferencesManager, PreferenceManager::PREF_DEFAULT_DOMAIN, 'defaultDomain', true);
79
-
80
-            $this->assign('canManualCreate',
81
-                $this->barrierTest(PreferenceManager::CREATION_MANUAL, $user, 'RequestCreation'));
82
-            $this->assign('canOauthCreate',
83
-                $this->barrierTest(PreferenceManager::CREATION_OAUTH, $user, 'RequestCreation'));
84
-            $this->assign('canBotCreate',
85
-                $this->barrierTest(PreferenceManager::CREATION_BOT, $user, 'RequestCreation'));
86
-
87
-            $oauth = new OAuthUserHelper($user, $database, $this->getOAuthProtocolHelper(),
88
-                $this->getSiteConfiguration());
89
-            $this->assign('oauth', $oauth);
90
-
91
-            $identity = null;
92
-            if ($oauth->isFullyLinked()) {
93
-                $identity = $oauth->getIdentity(true);
94
-            }
95
-
96
-            $this->assign('identity', $identity);
97
-            $this->assign('graceTime', $this->getSiteConfiguration()->getOauthIdentityGraceTime());
98
-        }
99
-    }
100
-
101
-    private function assignPreference(
102
-        PreferenceManager $preferencesManager,
103
-        string $preference,
104
-        string $fieldName,
105
-        bool $defaultGlobal,
106
-        $defaultValue = null
107
-    ): void {
108
-        $this->assign($fieldName, $preferencesManager->getPreference($preference) ?? $defaultValue);
109
-        $this->assign($fieldName . 'Global', $preferencesManager->isGlobalPreference($preference) ?? $defaultGlobal);
110
-    }
111
-
112
-    private function setPreferenceWithValue(
113
-        PreferenceManager $preferencesManager,
114
-        string $preferenceName,
115
-        string $fieldName,
116
-        $value
117
-    ): void {
118
-        $globalDefinition = WebRequest::postBoolean($fieldName . 'Global');
119
-        if ($globalDefinition) {
120
-            $preferencesManager->setGlobalPreference($preferenceName, $value);
121
-        }
122
-        else {
123
-            $preferencesManager->setLocalPreference($preferenceName, $value);
124
-        }
125
-    }
126
-
127
-    private function setPreference(
128
-        PreferenceManager $preferencesManager,
129
-        string $preferenceName,
130
-        string $fieldName
131
-    ): void {
132
-        $this->setPreferenceWithValue($preferencesManager, $preferenceName, $fieldName, WebRequest::postString($fieldName));
133
-    }
134
-
135
-    protected function refreshOAuth()
136
-    {
137
-        if (!WebRequest::wasPosted()) {
138
-            $this->redirect('preferences');
139
-
140
-            return;
141
-        }
142
-
143
-        $database = $this->getDatabase();
144
-        $oauth = new OAuthUserHelper(User::getCurrent($database), $database, $this->getOAuthProtocolHelper(),
145
-            $this->getSiteConfiguration());
146
-
147
-        // token is for old consumer, run through the approval workflow again
148
-        if ($oauth->getIdentity(true)->getAudience() !== $this->getSiteConfiguration()->getOAuthConsumerToken()) {
149
-            $authoriseUrl = $oauth->getRequestToken();
150
-            $this->redirectUrl($authoriseUrl);
151
-
152
-            return;
153
-        }
154
-
155
-        if ($oauth->isFullyLinked()) {
156
-            $oauth->refreshIdentity();
157
-        }
158
-
159
-        $this->redirect('preferences');
160
-
161
-        return;
162
-    }
163
-
164
-    private function setCreationMode(User $user, PreferenceManager $preferenceManager)
165
-    {
166
-        // if the user is selecting a creation mode that they are not allowed, do nothing.
167
-        // this has the side effect of allowing them to keep a selected mode that either has been changed for them,
168
-        // or that they have kept from when they previously had certain access.
169
-        // This setting is only settable locally, as ACLs may change between domains.
170
-        $creationMode = WebRequest::postInt('creationMode');
171
-        if ($this->barrierTest($creationMode, $user, 'RequestCreation')) {
172
-            $preferenceManager->setLocalPreference(PreferenceManager::PREF_CREATION_MODE, WebRequest::postString('creationMode'));
173
-        }
174
-    }
175
-
176
-    private function setSkin(PreferenceManager $preferencesManager): void
177
-    {
178
-        $newSkin = WebRequest::postString('skin');
179
-        $allowedSkins = ['main', 'alt', 'auto'];
180
-        if (in_array($newSkin, $allowedSkins)) {
181
-            $this->setPreference($preferencesManager, PreferenceManager::PREF_SKIN, 'skin');
182
-        }
183
-    }
23
+	/**
24
+	 * Main function for this page, when no specific actions are called.
25
+	 * @return void
26
+	 */
27
+	protected function main()
28
+	{
29
+		$this->setHtmlTitle('Preferences');
30
+
31
+		$enforceOAuth = $this->getSiteConfiguration()->getEnforceOAuth();
32
+		$database = $this->getDatabase();
33
+		$user = User::getCurrent($database);
34
+		$preferencesManager = PreferenceManager::getForCurrent($database);
35
+
36
+		// Dual mode
37
+		if (WebRequest::wasPosted()) {
38
+			$this->validateCSRFToken();
39
+
40
+			$this->setPreference($preferencesManager,PreferenceManager::PREF_EMAIL_SIGNATURE, 'emailSignature');
41
+			$this->setPreferenceWithValue($preferencesManager,PreferenceManager::PREF_SKIP_JS_ABORT, 'skipJsAbort', WebRequest::postBoolean('skipJsAbort') ? 1 : 0);
42
+			$this->setPreferenceWithValue($preferencesManager,PreferenceManager::PREF_QUEUE_HELP, 'showQueueHelp', WebRequest::postBoolean('showQueueHelp') ? 1 : 0);
43
+			$this->setCreationMode($user, $preferencesManager);
44
+			$this->setSkin($preferencesManager);
45
+			$preferencesManager->setGlobalPreference(PreferenceManager::PREF_DEFAULT_DOMAIN, WebRequest::postInt('defaultDomain'));
46
+
47
+			$email = WebRequest::postEmail('email');
48
+			if ($email !== null) {
49
+				$user->setEmail($email);
50
+			}
51
+
52
+			$user->save();
53
+			SessionAlert::success("Preferences updated!");
54
+
55
+			if ($this->barrierTest(RoleConfiguration::MAIN, $user, PageMain::class)) {
56
+				$this->redirect('');
57
+			}
58
+			else {
59
+				$this->redirect('preferences');
60
+			}
61
+		}
62
+		else {
63
+			$this->assignCSRFToken();
64
+			$this->setTemplate('preferences/prefs.tpl');
65
+
66
+			// FIXME: domains!
67
+			/** @var Domain $domain */
68
+			$domain = Domain::getById(1, $this->getDatabase());
69
+			$this->assign('mediawikiScriptPath', $domain->getWikiArticlePath());
70
+
71
+			$this->assign("enforceOAuth", $enforceOAuth);
72
+
73
+			$this->assignPreference($preferencesManager, PreferenceManager::PREF_EMAIL_SIGNATURE, 'emailSignature', false);
74
+			$this->assignPreference($preferencesManager, PreferenceManager::PREF_CREATION_MODE, 'creationMode', false);
75
+			$this->assignPreference($preferencesManager, PreferenceManager::PREF_SKIN, 'skin', true);
76
+			$this->assignPreference($preferencesManager, PreferenceManager::PREF_SKIP_JS_ABORT, 'skipJsAbort', false);
77
+			$this->assignPreference($preferencesManager, PreferenceManager::PREF_QUEUE_HELP, 'showQueueHelp', false, true);
78
+			$this->assignPreference($preferencesManager, PreferenceManager::PREF_DEFAULT_DOMAIN, 'defaultDomain', true);
79
+
80
+			$this->assign('canManualCreate',
81
+				$this->barrierTest(PreferenceManager::CREATION_MANUAL, $user, 'RequestCreation'));
82
+			$this->assign('canOauthCreate',
83
+				$this->barrierTest(PreferenceManager::CREATION_OAUTH, $user, 'RequestCreation'));
84
+			$this->assign('canBotCreate',
85
+				$this->barrierTest(PreferenceManager::CREATION_BOT, $user, 'RequestCreation'));
86
+
87
+			$oauth = new OAuthUserHelper($user, $database, $this->getOAuthProtocolHelper(),
88
+				$this->getSiteConfiguration());
89
+			$this->assign('oauth', $oauth);
90
+
91
+			$identity = null;
92
+			if ($oauth->isFullyLinked()) {
93
+				$identity = $oauth->getIdentity(true);
94
+			}
95
+
96
+			$this->assign('identity', $identity);
97
+			$this->assign('graceTime', $this->getSiteConfiguration()->getOauthIdentityGraceTime());
98
+		}
99
+	}
100
+
101
+	private function assignPreference(
102
+		PreferenceManager $preferencesManager,
103
+		string $preference,
104
+		string $fieldName,
105
+		bool $defaultGlobal,
106
+		$defaultValue = null
107
+	): void {
108
+		$this->assign($fieldName, $preferencesManager->getPreference($preference) ?? $defaultValue);
109
+		$this->assign($fieldName . 'Global', $preferencesManager->isGlobalPreference($preference) ?? $defaultGlobal);
110
+	}
111
+
112
+	private function setPreferenceWithValue(
113
+		PreferenceManager $preferencesManager,
114
+		string $preferenceName,
115
+		string $fieldName,
116
+		$value
117
+	): void {
118
+		$globalDefinition = WebRequest::postBoolean($fieldName . 'Global');
119
+		if ($globalDefinition) {
120
+			$preferencesManager->setGlobalPreference($preferenceName, $value);
121
+		}
122
+		else {
123
+			$preferencesManager->setLocalPreference($preferenceName, $value);
124
+		}
125
+	}
126
+
127
+	private function setPreference(
128
+		PreferenceManager $preferencesManager,
129
+		string $preferenceName,
130
+		string $fieldName
131
+	): void {
132
+		$this->setPreferenceWithValue($preferencesManager, $preferenceName, $fieldName, WebRequest::postString($fieldName));
133
+	}
134
+
135
+	protected function refreshOAuth()
136
+	{
137
+		if (!WebRequest::wasPosted()) {
138
+			$this->redirect('preferences');
139
+
140
+			return;
141
+		}
142
+
143
+		$database = $this->getDatabase();
144
+		$oauth = new OAuthUserHelper(User::getCurrent($database), $database, $this->getOAuthProtocolHelper(),
145
+			$this->getSiteConfiguration());
146
+
147
+		// token is for old consumer, run through the approval workflow again
148
+		if ($oauth->getIdentity(true)->getAudience() !== $this->getSiteConfiguration()->getOAuthConsumerToken()) {
149
+			$authoriseUrl = $oauth->getRequestToken();
150
+			$this->redirectUrl($authoriseUrl);
151
+
152
+			return;
153
+		}
154
+
155
+		if ($oauth->isFullyLinked()) {
156
+			$oauth->refreshIdentity();
157
+		}
158
+
159
+		$this->redirect('preferences');
160
+
161
+		return;
162
+	}
163
+
164
+	private function setCreationMode(User $user, PreferenceManager $preferenceManager)
165
+	{
166
+		// if the user is selecting a creation mode that they are not allowed, do nothing.
167
+		// this has the side effect of allowing them to keep a selected mode that either has been changed for them,
168
+		// or that they have kept from when they previously had certain access.
169
+		// This setting is only settable locally, as ACLs may change between domains.
170
+		$creationMode = WebRequest::postInt('creationMode');
171
+		if ($this->barrierTest($creationMode, $user, 'RequestCreation')) {
172
+			$preferenceManager->setLocalPreference(PreferenceManager::PREF_CREATION_MODE, WebRequest::postString('creationMode'));
173
+		}
174
+	}
175
+
176
+	private function setSkin(PreferenceManager $preferencesManager): void
177
+	{
178
+		$newSkin = WebRequest::postString('skin');
179
+		$allowedSkins = ['main', 'alt', 'auto'];
180
+		if (in_array($newSkin, $allowedSkins)) {
181
+			$this->setPreference($preferencesManager, PreferenceManager::PREF_SKIN, 'skin');
182
+		}
183
+	}
184 184
 }
Please login to merge, or discard this patch.
includes/Pages/UserAuth/MultiFactor/PageMultiFactor.php 1 patch
Indentation   +293 added lines, -293 removed lines patch added patch discarded remove patch
@@ -26,297 +26,297 @@
 block discarded – undo
26 26
 
27 27
 class PageMultiFactor extends InternalPageBase
28 28
 {
29
-    /**
30
-     * Main function for this page, when no specific actions are called.
31
-     * @return void
32
-     */
33
-    protected function main()
34
-    {
35
-        $database = $this->getDatabase();
36
-        $currentUser = User::getCurrent($database);
37
-
38
-        $yubikeyOtpCredentialProvider = new YubikeyOtpCredentialProvider($database, $this->getSiteConfiguration(),
39
-            $this->getHttpHelper());
40
-        $this->assign('yubikeyOtpIdentity', $yubikeyOtpCredentialProvider->getYubikeyData($currentUser->getId()));
41
-        $this->assign('yubikeyOtpEnrolled', $yubikeyOtpCredentialProvider->userIsEnrolled($currentUser->getId()));
42
-
43
-        $totpCredentialProvider = new TotpCredentialProvider($database, $this->getSiteConfiguration());
44
-        $this->assign('totpEnrolled', $totpCredentialProvider->userIsEnrolled($currentUser->getId()));
45
-
46
-        $scratchCredentialProvider = new ScratchTokenCredentialProvider($database, $this->getSiteConfiguration());
47
-        $this->assign('scratchEnrolled', $scratchCredentialProvider->userIsEnrolled($currentUser->getId()));
48
-        $this->assign('scratchRemaining', $scratchCredentialProvider->getRemaining($currentUser->getId()));
49
-
50
-        $this->assign('allowedTotp', $this->barrierTest('enableTotp', $currentUser));
51
-        $this->assign('allowedYubikey', $this->barrierTest('enableYubikeyOtp', $currentUser));
52
-
53
-        $this->setTemplate('mfa/mfa.tpl');
54
-    }
55
-
56
-    protected function enableYubikeyOtp()
57
-    {
58
-        $database = $this->getDatabase();
59
-        $currentUser = User::getCurrent($database);
60
-
61
-        $otpCredentialProvider = new YubikeyOtpCredentialProvider($database,
62
-            $this->getSiteConfiguration(), $this->getHttpHelper());
63
-
64
-        if (WebRequest::wasPosted()) {
65
-            $this->validateCSRFToken();
66
-
67
-            $passwordCredentialProvider = new PasswordCredentialProvider($database,
68
-                $this->getSiteConfiguration());
69
-
70
-            $password = WebRequest::postString('password');
71
-            $otp = WebRequest::postString('otp');
72
-
73
-            $result = $passwordCredentialProvider->authenticate($currentUser, $password);
74
-
75
-            if ($result) {
76
-                try {
77
-                    $otpCredentialProvider->setCredential($currentUser, 2, $otp);
78
-                    SessionAlert::success('Enabled YubiKey OTP.');
79
-
80
-                    $scratchProvider = new ScratchTokenCredentialProvider($database, $this->getSiteConfiguration());
81
-                    if ($scratchProvider->getRemaining($currentUser->getId()) < 3) {
82
-                        $scratchProvider->setCredential($currentUser, 2, null);
83
-                        $tokens = $scratchProvider->getTokens();
84
-                        $this->assign('tokens', $tokens);
85
-                        $this->setTemplate('mfa/regenScratchTokens.tpl');
86
-                        return;
87
-                    }
88
-                }
89
-                catch (ApplicationLogicException $ex) {
90
-                    SessionAlert::error('Error enabling YubiKey OTP: ' . $ex->getMessage());
91
-                }
92
-
93
-                $this->redirect('multiFactor');
94
-            }
95
-            else {
96
-                SessionAlert::error('Error enabling YubiKey OTP - invalid credentials.');
97
-                $this->redirect('multiFactor');
98
-            }
99
-        }
100
-        else {
101
-            if ($otpCredentialProvider->userIsEnrolled($currentUser->getId())) {
102
-                // user is not enrolled, we shouldn't have got here.
103
-                throw new ApplicationLogicException('User is already enrolled in the selected MFA mechanism');
104
-            }
105
-
106
-            $this->assignCSRFToken();
107
-            $this->setTemplate('mfa/enableYubikey.tpl');
108
-        }
109
-    }
110
-
111
-    protected function disableYubikeyOtp()
112
-    {
113
-        $database = $this->getDatabase();
114
-        $currentUser = User::getCurrent($database);
115
-
116
-        $otpCredentialProvider = new YubikeyOtpCredentialProvider($database,
117
-            $this->getSiteConfiguration(), $this->getHttpHelper());
118
-
119
-        $factorType = 'YubiKey OTP';
120
-
121
-        $this->deleteCredential($database, $currentUser, $otpCredentialProvider, $factorType);
122
-    }
123
-
124
-    protected function enableTotp()
125
-    {
126
-        $database = $this->getDatabase();
127
-        $currentUser = User::getCurrent($database);
128
-
129
-        $otpCredentialProvider = new TotpCredentialProvider($database, $this->getSiteConfiguration());
130
-
131
-        if (WebRequest::wasPosted()) {
132
-            $this->validateCSRFToken();
133
-
134
-            // used for routing only, not security
135
-            $stage = WebRequest::postString('stage');
136
-
137
-            if ($stage === "auth") {
138
-                $password = WebRequest::postString('password');
139
-
140
-                $passwordCredentialProvider = new PasswordCredentialProvider($database,
141
-                    $this->getSiteConfiguration());
142
-                $result = $passwordCredentialProvider->authenticate($currentUser, $password);
143
-
144
-                if ($result) {
145
-                    $otpCredentialProvider->setCredential($currentUser, 2, null);
146
-
147
-                    $provisioningUrl = $otpCredentialProvider->getProvisioningUrl($currentUser);
148
-
149
-                    $renderer = new ImageRenderer(
150
-                        new RendererStyle(256),
151
-                        new SvgImageBackEnd()
152
-                    );
153
-
154
-                    $writer = new Writer($renderer);
155
-                    $svg = $writer->writeString($provisioningUrl);
156
-
157
-                    $this->assign('svg', $svg);
158
-                    $this->assign('secret', $otpCredentialProvider->getSecret($currentUser));
159
-
160
-                    $this->assignCSRFToken();
161
-                    $this->setTemplate('mfa/enableTotpEnroll.tpl');
162
-
163
-                    return;
164
-                }
165
-                else {
166
-                    SessionAlert::error('Error enabling TOTP - invalid credentials.');
167
-                    $this->redirect('multiFactor');
168
-
169
-                    return;
170
-                }
171
-            }
172
-
173
-            if ($stage === "enroll") {
174
-                // we *must* have a defined credential already here,
175
-                if ($otpCredentialProvider->isPartiallyEnrolled($currentUser)) {
176
-                    $otp = WebRequest::postString('otp');
177
-                    $result = $otpCredentialProvider->verifyEnable($currentUser, $otp);
178
-
179
-                    if ($result) {
180
-                        SessionAlert::success('Enabled TOTP.');
181
-
182
-                        $scratchProvider = new ScratchTokenCredentialProvider($database, $this->getSiteConfiguration());
183
-                        if ($scratchProvider->getRemaining($currentUser->getId()) < 3) {
184
-                            $scratchProvider->setCredential($currentUser, 2, null);
185
-                            $tokens = $scratchProvider->getTokens();
186
-                            $this->assign('tokens', $tokens);
187
-                            $this->setTemplate('mfa/regenScratchTokens.tpl');
188
-                            return;
189
-                        }
190
-                    }
191
-                    else {
192
-                        $otpCredentialProvider->deleteCredential($currentUser);
193
-                        SessionAlert::error('Error enabling TOTP: invalid token provided');
194
-                    }
195
-
196
-
197
-                    $this->redirect('multiFactor');
198
-                    return;
199
-                }
200
-                else {
201
-                    SessionAlert::error('Error enabling TOTP - no enrollment found or enrollment expired.');
202
-                    $this->redirect('multiFactor');
203
-
204
-                    return;
205
-                }
206
-            }
207
-
208
-            // urgh, dunno what happened, but it's not something expected.
209
-            throw new ApplicationLogicException();
210
-        }
211
-        else {
212
-            if ($otpCredentialProvider->userIsEnrolled($currentUser->getId())) {
213
-                // user is not enrolled, we shouldn't have got here.
214
-                throw new ApplicationLogicException('User is already enrolled in the selected MFA mechanism');
215
-            }
216
-
217
-            $this->assignCSRFToken();
218
-
219
-            $this->assign('alertmessage', 'To enable your multi-factor credentials, please prove you are who you say you are by providing your tool password below.');
220
-            $this->assign('alertheader', 'Provide credentials');
221
-            $this->assign('continueText', 'Verify password');
222
-            $this->setTemplate('mfa/enableAuth.tpl');
223
-        }
224
-    }
225
-
226
-    protected function disableTotp()
227
-    {
228
-        $database = $this->getDatabase();
229
-        $currentUser = User::getCurrent($database);
230
-
231
-        $otpCredentialProvider = new TotpCredentialProvider($database, $this->getSiteConfiguration());
232
-
233
-        $factorType = 'TOTP';
234
-
235
-        $this->deleteCredential($database, $currentUser, $otpCredentialProvider, $factorType);
236
-    }
237
-
238
-    protected function scratch()
239
-    {
240
-        $database = $this->getDatabase();
241
-        $currentUser = User::getCurrent($database);
242
-
243
-        if (WebRequest::wasPosted()) {
244
-            $this->validateCSRFToken();
245
-
246
-            $passwordCredentialProvider = new PasswordCredentialProvider($database,
247
-                $this->getSiteConfiguration());
248
-
249
-            $otpCredentialProvider = new ScratchTokenCredentialProvider($database,
250
-                $this->getSiteConfiguration());
251
-
252
-            $password = WebRequest::postString('password');
253
-
254
-            $result = $passwordCredentialProvider->authenticate($currentUser, $password);
255
-
256
-            if ($result) {
257
-                $otpCredentialProvider->setCredential($currentUser, 2, null);
258
-                $tokens = $otpCredentialProvider->getTokens();
259
-                $this->assign('tokens', $tokens);
260
-                $this->setTemplate('mfa/regenScratchTokens.tpl');
261
-            }
262
-            else {
263
-                SessionAlert::error('Error refreshing scratch tokens - invalid credentials.');
264
-                $this->redirect('multiFactor');
265
-            }
266
-        }
267
-        else {
268
-            $this->assignCSRFToken();
269
-
270
-            $this->assign('alertmessage', 'To regenerate your emergency scratch tokens, please prove you are who you say you are by providing your tool password below. Note that continuing will invalidate all remaining scratch tokens, and provide a set of new ones.');
271
-            $this->assign('alertheader', 'Re-generate scratch tokens');
272
-            $this->assign('continueText', 'Regenerate Scratch Tokens');
273
-
274
-            $this->setTemplate('mfa/enableAuth.tpl');
275
-        }
276
-    }
277
-
278
-    /**
279
-     * @param PdoDatabase         $database
280
-     * @param User                $currentUser
281
-     * @param ICredentialProvider $otpCredentialProvider
282
-     * @param string              $factorType
283
-     *
284
-     * @throws ApplicationLogicException
285
-     */
286
-    private function deleteCredential(
287
-        PdoDatabase $database,
288
-        User $currentUser,
289
-        ICredentialProvider $otpCredentialProvider,
290
-        $factorType
291
-    ) {
292
-        if (WebRequest::wasPosted()) {
293
-            $passwordCredentialProvider = new PasswordCredentialProvider($database,
294
-                $this->getSiteConfiguration());
295
-
296
-            $this->validateCSRFToken();
297
-
298
-            $password = WebRequest::postString('password');
299
-            $result = $passwordCredentialProvider->authenticate($currentUser, $password);
300
-
301
-            if ($result) {
302
-                $otpCredentialProvider->deleteCredential($currentUser);
303
-                SessionAlert::success('Disabled ' . $factorType . '.');
304
-                $this->redirect('multiFactor');
305
-            }
306
-            else {
307
-                SessionAlert::error('Error disabling ' . $factorType . ' - invalid credentials.');
308
-                $this->redirect('multiFactor');
309
-            }
310
-        }
311
-        else {
312
-            if (!$otpCredentialProvider->userIsEnrolled($currentUser->getId())) {
313
-                // user is not enrolled, we shouldn't have got here.
314
-                throw new ApplicationLogicException('User is not enrolled in the selected MFA mechanism');
315
-            }
316
-
317
-            $this->assignCSRFToken();
318
-            $this->assign('otpType', $factorType);
319
-            $this->setTemplate('mfa/disableOtp.tpl');
320
-        }
321
-    }
29
+	/**
30
+	 * Main function for this page, when no specific actions are called.
31
+	 * @return void
32
+	 */
33
+	protected function main()
34
+	{
35
+		$database = $this->getDatabase();
36
+		$currentUser = User::getCurrent($database);
37
+
38
+		$yubikeyOtpCredentialProvider = new YubikeyOtpCredentialProvider($database, $this->getSiteConfiguration(),
39
+			$this->getHttpHelper());
40
+		$this->assign('yubikeyOtpIdentity', $yubikeyOtpCredentialProvider->getYubikeyData($currentUser->getId()));
41
+		$this->assign('yubikeyOtpEnrolled', $yubikeyOtpCredentialProvider->userIsEnrolled($currentUser->getId()));
42
+
43
+		$totpCredentialProvider = new TotpCredentialProvider($database, $this->getSiteConfiguration());
44
+		$this->assign('totpEnrolled', $totpCredentialProvider->userIsEnrolled($currentUser->getId()));
45
+
46
+		$scratchCredentialProvider = new ScratchTokenCredentialProvider($database, $this->getSiteConfiguration());
47
+		$this->assign('scratchEnrolled', $scratchCredentialProvider->userIsEnrolled($currentUser->getId()));
48
+		$this->assign('scratchRemaining', $scratchCredentialProvider->getRemaining($currentUser->getId()));
49
+
50
+		$this->assign('allowedTotp', $this->barrierTest('enableTotp', $currentUser));
51
+		$this->assign('allowedYubikey', $this->barrierTest('enableYubikeyOtp', $currentUser));
52
+
53
+		$this->setTemplate('mfa/mfa.tpl');
54
+	}
55
+
56
+	protected function enableYubikeyOtp()
57
+	{
58
+		$database = $this->getDatabase();
59
+		$currentUser = User::getCurrent($database);
60
+
61
+		$otpCredentialProvider = new YubikeyOtpCredentialProvider($database,
62
+			$this->getSiteConfiguration(), $this->getHttpHelper());
63
+
64
+		if (WebRequest::wasPosted()) {
65
+			$this->validateCSRFToken();
66
+
67
+			$passwordCredentialProvider = new PasswordCredentialProvider($database,
68
+				$this->getSiteConfiguration());
69
+
70
+			$password = WebRequest::postString('password');
71
+			$otp = WebRequest::postString('otp');
72
+
73
+			$result = $passwordCredentialProvider->authenticate($currentUser, $password);
74
+
75
+			if ($result) {
76
+				try {
77
+					$otpCredentialProvider->setCredential($currentUser, 2, $otp);
78
+					SessionAlert::success('Enabled YubiKey OTP.');
79
+
80
+					$scratchProvider = new ScratchTokenCredentialProvider($database, $this->getSiteConfiguration());
81
+					if ($scratchProvider->getRemaining($currentUser->getId()) < 3) {
82
+						$scratchProvider->setCredential($currentUser, 2, null);
83
+						$tokens = $scratchProvider->getTokens();
84
+						$this->assign('tokens', $tokens);
85
+						$this->setTemplate('mfa/regenScratchTokens.tpl');
86
+						return;
87
+					}
88
+				}
89
+				catch (ApplicationLogicException $ex) {
90
+					SessionAlert::error('Error enabling YubiKey OTP: ' . $ex->getMessage());
91
+				}
92
+
93
+				$this->redirect('multiFactor');
94
+			}
95
+			else {
96
+				SessionAlert::error('Error enabling YubiKey OTP - invalid credentials.');
97
+				$this->redirect('multiFactor');
98
+			}
99
+		}
100
+		else {
101
+			if ($otpCredentialProvider->userIsEnrolled($currentUser->getId())) {
102
+				// user is not enrolled, we shouldn't have got here.
103
+				throw new ApplicationLogicException('User is already enrolled in the selected MFA mechanism');
104
+			}
105
+
106
+			$this->assignCSRFToken();
107
+			$this->setTemplate('mfa/enableYubikey.tpl');
108
+		}
109
+	}
110
+
111
+	protected function disableYubikeyOtp()
112
+	{
113
+		$database = $this->getDatabase();
114
+		$currentUser = User::getCurrent($database);
115
+
116
+		$otpCredentialProvider = new YubikeyOtpCredentialProvider($database,
117
+			$this->getSiteConfiguration(), $this->getHttpHelper());
118
+
119
+		$factorType = 'YubiKey OTP';
120
+
121
+		$this->deleteCredential($database, $currentUser, $otpCredentialProvider, $factorType);
122
+	}
123
+
124
+	protected function enableTotp()
125
+	{
126
+		$database = $this->getDatabase();
127
+		$currentUser = User::getCurrent($database);
128
+
129
+		$otpCredentialProvider = new TotpCredentialProvider($database, $this->getSiteConfiguration());
130
+
131
+		if (WebRequest::wasPosted()) {
132
+			$this->validateCSRFToken();
133
+
134
+			// used for routing only, not security
135
+			$stage = WebRequest::postString('stage');
136
+
137
+			if ($stage === "auth") {
138
+				$password = WebRequest::postString('password');
139
+
140
+				$passwordCredentialProvider = new PasswordCredentialProvider($database,
141
+					$this->getSiteConfiguration());
142
+				$result = $passwordCredentialProvider->authenticate($currentUser, $password);
143
+
144
+				if ($result) {
145
+					$otpCredentialProvider->setCredential($currentUser, 2, null);
146
+
147
+					$provisioningUrl = $otpCredentialProvider->getProvisioningUrl($currentUser);
148
+
149
+					$renderer = new ImageRenderer(
150
+						new RendererStyle(256),
151
+						new SvgImageBackEnd()
152
+					);
153
+
154
+					$writer = new Writer($renderer);
155
+					$svg = $writer->writeString($provisioningUrl);
156
+
157
+					$this->assign('svg', $svg);
158
+					$this->assign('secret', $otpCredentialProvider->getSecret($currentUser));
159
+
160
+					$this->assignCSRFToken();
161
+					$this->setTemplate('mfa/enableTotpEnroll.tpl');
162
+
163
+					return;
164
+				}
165
+				else {
166
+					SessionAlert::error('Error enabling TOTP - invalid credentials.');
167
+					$this->redirect('multiFactor');
168
+
169
+					return;
170
+				}
171
+			}
172
+
173
+			if ($stage === "enroll") {
174
+				// we *must* have a defined credential already here,
175
+				if ($otpCredentialProvider->isPartiallyEnrolled($currentUser)) {
176
+					$otp = WebRequest::postString('otp');
177
+					$result = $otpCredentialProvider->verifyEnable($currentUser, $otp);
178
+
179
+					if ($result) {
180
+						SessionAlert::success('Enabled TOTP.');
181
+
182
+						$scratchProvider = new ScratchTokenCredentialProvider($database, $this->getSiteConfiguration());
183
+						if ($scratchProvider->getRemaining($currentUser->getId()) < 3) {
184
+							$scratchProvider->setCredential($currentUser, 2, null);
185
+							$tokens = $scratchProvider->getTokens();
186
+							$this->assign('tokens', $tokens);
187
+							$this->setTemplate('mfa/regenScratchTokens.tpl');
188
+							return;
189
+						}
190
+					}
191
+					else {
192
+						$otpCredentialProvider->deleteCredential($currentUser);
193
+						SessionAlert::error('Error enabling TOTP: invalid token provided');
194
+					}
195
+
196
+
197
+					$this->redirect('multiFactor');
198
+					return;
199
+				}
200
+				else {
201
+					SessionAlert::error('Error enabling TOTP - no enrollment found or enrollment expired.');
202
+					$this->redirect('multiFactor');
203
+
204
+					return;
205
+				}
206
+			}
207
+
208
+			// urgh, dunno what happened, but it's not something expected.
209
+			throw new ApplicationLogicException();
210
+		}
211
+		else {
212
+			if ($otpCredentialProvider->userIsEnrolled($currentUser->getId())) {
213
+				// user is not enrolled, we shouldn't have got here.
214
+				throw new ApplicationLogicException('User is already enrolled in the selected MFA mechanism');
215
+			}
216
+
217
+			$this->assignCSRFToken();
218
+
219
+			$this->assign('alertmessage', 'To enable your multi-factor credentials, please prove you are who you say you are by providing your tool password below.');
220
+			$this->assign('alertheader', 'Provide credentials');
221
+			$this->assign('continueText', 'Verify password');
222
+			$this->setTemplate('mfa/enableAuth.tpl');
223
+		}
224
+	}
225
+
226
+	protected function disableTotp()
227
+	{
228
+		$database = $this->getDatabase();
229
+		$currentUser = User::getCurrent($database);
230
+
231
+		$otpCredentialProvider = new TotpCredentialProvider($database, $this->getSiteConfiguration());
232
+
233
+		$factorType = 'TOTP';
234
+
235
+		$this->deleteCredential($database, $currentUser, $otpCredentialProvider, $factorType);
236
+	}
237
+
238
+	protected function scratch()
239
+	{
240
+		$database = $this->getDatabase();
241
+		$currentUser = User::getCurrent($database);
242
+
243
+		if (WebRequest::wasPosted()) {
244
+			$this->validateCSRFToken();
245
+
246
+			$passwordCredentialProvider = new PasswordCredentialProvider($database,
247
+				$this->getSiteConfiguration());
248
+
249
+			$otpCredentialProvider = new ScratchTokenCredentialProvider($database,
250
+				$this->getSiteConfiguration());
251
+
252
+			$password = WebRequest::postString('password');
253
+
254
+			$result = $passwordCredentialProvider->authenticate($currentUser, $password);
255
+
256
+			if ($result) {
257
+				$otpCredentialProvider->setCredential($currentUser, 2, null);
258
+				$tokens = $otpCredentialProvider->getTokens();
259
+				$this->assign('tokens', $tokens);
260
+				$this->setTemplate('mfa/regenScratchTokens.tpl');
261
+			}
262
+			else {
263
+				SessionAlert::error('Error refreshing scratch tokens - invalid credentials.');
264
+				$this->redirect('multiFactor');
265
+			}
266
+		}
267
+		else {
268
+			$this->assignCSRFToken();
269
+
270
+			$this->assign('alertmessage', 'To regenerate your emergency scratch tokens, please prove you are who you say you are by providing your tool password below. Note that continuing will invalidate all remaining scratch tokens, and provide a set of new ones.');
271
+			$this->assign('alertheader', 'Re-generate scratch tokens');
272
+			$this->assign('continueText', 'Regenerate Scratch Tokens');
273
+
274
+			$this->setTemplate('mfa/enableAuth.tpl');
275
+		}
276
+	}
277
+
278
+	/**
279
+	 * @param PdoDatabase         $database
280
+	 * @param User                $currentUser
281
+	 * @param ICredentialProvider $otpCredentialProvider
282
+	 * @param string              $factorType
283
+	 *
284
+	 * @throws ApplicationLogicException
285
+	 */
286
+	private function deleteCredential(
287
+		PdoDatabase $database,
288
+		User $currentUser,
289
+		ICredentialProvider $otpCredentialProvider,
290
+		$factorType
291
+	) {
292
+		if (WebRequest::wasPosted()) {
293
+			$passwordCredentialProvider = new PasswordCredentialProvider($database,
294
+				$this->getSiteConfiguration());
295
+
296
+			$this->validateCSRFToken();
297
+
298
+			$password = WebRequest::postString('password');
299
+			$result = $passwordCredentialProvider->authenticate($currentUser, $password);
300
+
301
+			if ($result) {
302
+				$otpCredentialProvider->deleteCredential($currentUser);
303
+				SessionAlert::success('Disabled ' . $factorType . '.');
304
+				$this->redirect('multiFactor');
305
+			}
306
+			else {
307
+				SessionAlert::error('Error disabling ' . $factorType . ' - invalid credentials.');
308
+				$this->redirect('multiFactor');
309
+			}
310
+		}
311
+		else {
312
+			if (!$otpCredentialProvider->userIsEnrolled($currentUser->getId())) {
313
+				// user is not enrolled, we shouldn't have got here.
314
+				throw new ApplicationLogicException('User is not enrolled in the selected MFA mechanism');
315
+			}
316
+
317
+			$this->assignCSRFToken();
318
+			$this->assign('otpType', $factorType);
319
+			$this->setTemplate('mfa/disableOtp.tpl');
320
+		}
321
+	}
322 322
 }
Please login to merge, or discard this patch.
includes/Pages/UserAuth/PageLogout.php 1 patch
Indentation   +16 added lines, -16 removed lines patch added patch discarded remove patch
@@ -14,22 +14,22 @@
 block discarded – undo
14 14
 
15 15
 class PageLogout extends InternalPageBase
16 16
 {
17
-    /**
18
-     * Main function for this page, when no specific actions are called.
19
-     */
20
-    protected function main()
21
-    {
22
-        if (WebRequest::wasPosted()) {
23
-            Session::destroy();
24
-            $this->redirect("login");
25
-            return;
26
-        }
17
+	/**
18
+	 * Main function for this page, when no specific actions are called.
19
+	 */
20
+	protected function main()
21
+	{
22
+		if (WebRequest::wasPosted()) {
23
+			Session::destroy();
24
+			$this->redirect("login");
25
+			return;
26
+		}
27 27
 
28
-        $this->redirect();
29
-    }
28
+		$this->redirect();
29
+	}
30 30
 
31
-    protected function isProtectedPage()
32
-    {
33
-        return false;
34
-    }
31
+	protected function isProtectedPage()
32
+	{
33
+		return false;
34
+	}
35 35
 }
Please login to merge, or discard this patch.