Issues (186)

includes/WebStart.php (1 issue)

Severity
1
<?php
2
/******************************************************************************
3
 * Wikipedia Account Creation Assistance tool                                 *
4
 * ACC Development Team. Please see team.json for a list of contributors.     *
5
 *                                                                            *
6
 * This is free and unencumbered software released into the public domain.    *
7
 * Please see LICENSE.md for the full licencing statement.                    *
8
 ******************************************************************************/
9
10
namespace Waca;
11
12
use Waca\DataObjects\Domain;
13
use Waca\DataObjects\User;
14
use Waca\Exceptions\EnvironmentException;
15
use Waca\Exceptions\ReadableException;
16
use Waca\Helpers\BlacklistHelper;
17
use Waca\Helpers\FakeBlacklistHelper;
18
use Waca\Helpers\TypeAheadHelper;
19
use Waca\Providers\GlobalState\GlobalStateProvider;
20
use Waca\Router\IRequestRouter;
21
use Waca\Security\ContentSecurityPolicyManager;
22
use Waca\Security\DomainAccessManager;
23
use Waca\Security\RoleConfiguration;
24
use Waca\Security\SecurityManager;
25
use Waca\Security\TokenManager;
26
use Waca\Security\UserAccessLoader;
27
use Waca\Tasks\ITask;
28
use Waca\Tasks\InternalPageBase;
29
use Waca\Tasks\PageBase;
30
31
/**
32
 * Application entry point.
33
 *
34
 * @package Waca
35
 */
36
class WebStart extends ApplicationBase
37
{
38
    /**
39
     * @var IRequestRouter $requestRouter The request router to use. Note that different entry points have different
40
     *                                    routers and hence different URL mappings
41
     */
42
    private $requestRouter;
43
    /**
44
     * @var bool $isPublic Determines whether to use public interface objects or internal interface objects
45
     */
46
    private bool $isPublic = false;
47
48
    /**
49
     * WebStart constructor.
50
     *
51
     * @param SiteConfiguration $configuration The site configuration
52
     * @param IRequestRouter    $router        The request router to use
53
     */
54
    public function __construct(SiteConfiguration $configuration, IRequestRouter $router)
55
    {
56
        parent::__construct($configuration);
57
58
        $this->requestRouter = $router;
59
    }
60
61
    /**
62
     * @param ITask             $page
63
     * @param SiteConfiguration $siteConfiguration
64
     * @param PdoDatabase       $database
65
     *
66
     * @return void
67
     */
68
    protected function setupHelpers(
69
        ITask $page,
70
        SiteConfiguration $siteConfiguration,
71
        PdoDatabase $database
72
    ) {
73
        parent::setupHelpers($page, $siteConfiguration, $database);
74
75
        if ($page instanceof PageBase) {
76
            $page->setTokenManager(new TokenManager());
77
            $page->setCspManager(new ContentSecurityPolicyManager($siteConfiguration));
78
79
            if ($page instanceof InternalPageBase) {
80
                $page->setTypeAheadHelper(new TypeAheadHelper());
81
82
                $httpHelper = $page->getHttpHelper();
83
84
                $userAccessLoader = new UserAccessLoader();
85
                $domainAccessManager = new DomainAccessManager($userAccessLoader);
86
87
                $identificationVerifier = new IdentificationVerifier($httpHelper, $siteConfiguration, $database);
88
89
                $roleConfiguration = new RoleConfiguration($siteConfiguration->getGlobalDenyRole());
90
                $page->setSecurityManager(new SecurityManager($identificationVerifier, $roleConfiguration, $userAccessLoader));
91
                $page->setDomainAccessManager($domainAccessManager);
92
93
                if ($siteConfiguration->getTitleBlacklistEnabled()) {
94
                    $page->setBlacklistHelper(new BlacklistHelper($httpHelper, $database, $siteConfiguration));
95
                }
96
                else {
97
                    $page->setBlacklistHelper(new FakeBlacklistHelper());
98
                }
99
            }
100
        }
101
    }
102
103
    /**
104
     * Application entry point.
105
     *
106
     * Sets up the environment and runs the application, performing any global cleanup operations when done.
107
     */
108
    public function run()
109
    {
110
        try {
111
            if ($this->setupEnvironment()) {
112
                $this->main();
113
            }
114
        }
115
        catch (EnvironmentException $ex) {
116
            ob_end_clean();
117
            print Offline::getOfflineMessage($this->isPublic(), $this->getConfiguration(), $ex->getMessage());
118
        }
119
            /** @noinspection PhpRedundantCatchClauseInspection */
120
        catch (ReadableException $ex) {
121
            ob_end_clean();
122
            print $ex->getReadableError();
123
        }
124
        finally {
125
            $this->cleanupEnvironment();
126
        }
127
    }
128
129
    /**
130
     * Environment setup
131
     *
132
     * This method initialises the tool environment. If the tool cannot be initialised correctly, it will return false
133
     * and shut down prematurely.
134
     *
135
     * @return bool
136
     * @throws EnvironmentException
137
     */
138
    protected function setupEnvironment()
139
    {
140
        // initialise global exception handler
141
        set_exception_handler(array(ExceptionHandler::class, 'exceptionHandler'));
142
        set_error_handler(array(ExceptionHandler::class, 'errorHandler'), E_RECOVERABLE_ERROR);
143
144
        // start output buffering if necessary
145
        if (ob_get_level() === 0) {
146
            ob_start();
147
        }
148
149
        // initialise super-global providers
150
        WebRequest::setGlobalStateProvider(new GlobalStateProvider());
151
152
        if (Offline::isOffline($this->getConfiguration())) {
153
            print Offline::getOfflineMessage($this->isPublic(), $this->getConfiguration());
154
            ob_end_flush();
155
156
            return false;
157
        }
158
159
        // Call parent setup
160
        if (!parent::setupEnvironment()) {
161
            return false;
162
        }
163
164
        // Start up sessions
165
        ini_set('session.cookie_path', $this->getConfiguration()->getCookiePath());
166
        ini_set('session.name', $this->getConfiguration()->getCookieSessionName());
167
        Session::start();
168
169
        // Check the user is allowed to be logged in still. This must be before we call any user-loading functions and
170
        // get the current user cached.
171
        // I'm not sure if this function call being here is particularly a good thing, but it's part of starting up a
172
        // session I suppose.
173
        $this->checkForceLogout();
174
175
        // environment initialised!
176
        return true;
177
    }
178
179
    /**
180
     * Main application logic
181
     */
182
    protected function main()
183
    {
184
        // Get the right route for the request
185
        $page = $this->requestRouter->route();
186
187
        $siteConfiguration = $this->getConfiguration();
188
        $database = PdoDatabase::getDatabaseConnection($this->getConfiguration());
189
190
        $this->setupHelpers($page, $siteConfiguration, $database);
191
192
        // run the route code for the request.
193
        $page->execute();
194
    }
195
196
    /**
197
     * Any cleanup tasks should go here
198
     *
199
     * Note that we need to be very careful here, as exceptions may have been thrown and handled.
200
     * This should *only* be for cleaning up, no logic should go here.
201
     */
202
    protected function cleanupEnvironment()
203
    {
204
        // Clean up anything we splurged after sending the page.
205
        if (ob_get_level() > 0) {
206
            for ($i = ob_get_level(); $i > 0; $i--) {
207
                ob_end_clean();
208
            }
209
        }
210
    }
211
212
    private function checkForceLogout()
213
    {
214
        $database = PdoDatabase::getDatabaseConnection($this->getConfiguration());
215
216
        $sessionUserId = WebRequest::getSessionUserId();
217
        iF ($sessionUserId === null) {
218
            return;
219
        }
220
221
        // Note, User::getCurrent() caches it's result, which we *really* don't want to trigger.
222
        $currentUser = User::getById($sessionUserId, $database);
223
224
        if ($currentUser === false) {
0 ignored issues
show
The condition $currentUser === false is always false.
Loading history...
225
            // Umm... this user has a session cookie with a userId set, but no user exists...
226
            Session::restart();
227
228
            $currentUser = User::getCurrent($database);
229
        }
230
231
        if ($currentUser->getForceLogout()) {
232
            Session::restart();
233
234
            $currentUser->setForceLogout(false);
235
            $currentUser->save();
236
        }
237
    }
238
239
    public function isPublic(): bool
240
    {
241
        return $this->isPublic;
242
    }
243
244
    public function setPublic(bool $isPublic): void
245
    {
246
        $this->isPublic = $isPublic;
247
    }
248
}
249