Issues (185)

includes/WebStart.php (1 issue)

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
    private ContentSecurityPolicyManager $cspManager;
49
50
    /**
51
     * WebStart constructor.
52
     *
53
     * @param SiteConfiguration $configuration The site configuration
54
     * @param IRequestRouter    $router        The request router to use
55
     */
56
    public function __construct(SiteConfiguration $configuration, IRequestRouter $router)
57
    {
58
        parent::__construct($configuration);
59
60
        $this->requestRouter = $router;
61
        $this->cspManager = new ContentSecurityPolicyManager($configuration);
62
    }
63
64
    /**
65
     * @param ITask             $page
66
     * @param SiteConfiguration $siteConfiguration
67
     * @param PdoDatabase       $database
68
     *
69
     * @return void
70
     */
71
    protected function setupHelpers(
72
        ITask $page,
73
        SiteConfiguration $siteConfiguration,
74
        PdoDatabase $database
75
    ) {
76
        parent::setupHelpers($page, $siteConfiguration, $database);
77
78
        if ($page instanceof PageBase) {
79
            $page->setTokenManager(new TokenManager());
80
            $page->setCspManager($this->cspManager);
81
82
            if ($page instanceof InternalPageBase) {
83
                $page->setTypeAheadHelper(new TypeAheadHelper());
84
85
                $httpHelper = $page->getHttpHelper();
86
87
                $userAccessLoader = new UserAccessLoader();
88
                $domainAccessManager = new DomainAccessManager($userAccessLoader);
89
90
                $identificationVerifier = new IdentificationVerifier($httpHelper, $siteConfiguration, $database);
91
92
                $roleConfiguration = new RoleConfiguration($siteConfiguration->getGlobalDenyRole());
93
                $page->setSecurityManager(new SecurityManager($identificationVerifier, $roleConfiguration, $userAccessLoader));
94
                $page->setDomainAccessManager($domainAccessManager);
95
96
                if ($siteConfiguration->getTitleBlacklistEnabled()) {
97
                    $page->setBlacklistHelper(new BlacklistHelper($httpHelper, $database, $siteConfiguration));
98
                }
99
                else {
100
                    $page->setBlacklistHelper(new FakeBlacklistHelper());
101
                }
102
            }
103
        }
104
    }
105
106
    /**
107
     * Application entry point.
108
     *
109
     * Sets up the environment and runs the application, performing any global cleanup operations when done.
110
     */
111
    public function run()
112
    {
113
        try {
114
            if ($this->setupEnvironment()) {
115
                $this->main();
116
            }
117
        }
118
        catch (EnvironmentException $ex) {
119
            ob_end_clean();
120
121
            if (!headers_sent()) {
122
                header($this->cspManager->getHeader());
123
            }
124
125
            print Offline::getOfflineMessage($this->isPublic(), $this->getConfiguration(), $ex->getMessage());
126
        }
127
        /** @noinspection PhpRedundantCatchClauseInspection */
128
        catch (ReadableException $ex) {
129
            ob_end_clean();
130
131
            if (!headers_sent()) {
132
                header($this->cspManager->getHeader());
133
            }
134
135
            print $ex->getReadableError();
136
        }
137
        finally {
138
            $this->cleanupEnvironment();
139
        }
140
    }
141
142
    /**
143
     * Environment setup
144
     *
145
     * This method initialises the tool environment. If the tool cannot be initialised correctly, it will return false
146
     * and shut down prematurely.
147
     *
148
     * @return bool
149
     * @throws EnvironmentException
150
     */
151
    protected function setupEnvironment()
152
    {
153
        // initialise global exception handler
154
        set_exception_handler(array(ExceptionHandler::class, 'exceptionHandler'));
155
        set_error_handler(array(ExceptionHandler::class, 'errorHandler'), E_RECOVERABLE_ERROR);
156
157
        // start output buffering if necessary
158
        if (ob_get_level() === 0) {
159
            ob_start();
160
        }
161
162
        // initialise super-global providers
163
        WebRequest::setGlobalStateProvider(new GlobalStateProvider());
164
165
        if (Offline::isOffline($this->getConfiguration())) {
166
            print Offline::getOfflineMessage($this->isPublic(), $this->getConfiguration());
167
            ob_end_flush();
168
169
            return false;
170
        }
171
172
        // Call parent setup
173
        if (!parent::setupEnvironment()) {
174
            return false;
175
        }
176
177
        // Start up sessions
178
        ini_set('session.cookie_path', $this->getConfiguration()->getCookiePath());
179
        ini_set('session.name', $this->getConfiguration()->getCookieSessionName());
180
        Session::start();
181
182
        // Check the user is allowed to be logged in still. This must be before we call any user-loading functions and
183
        // get the current user cached.
184
        // I'm not sure if this function call being here is particularly a good thing, but it's part of starting up a
185
        // session I suppose.
186
        $this->checkForceLogout();
187
188
        // environment initialised!
189
        return true;
190
    }
191
192
    /**
193
     * Main application logic
194
     */
195
    protected function main()
196
    {
197
        // Get the right route for the request
198
        $page = $this->requestRouter->route();
199
200
        $siteConfiguration = $this->getConfiguration();
201
        $database = PdoDatabase::getDatabaseConnection($this->getConfiguration());
202
203
        $this->setupHelpers($page, $siteConfiguration, $database);
204
205
        // run the route code for the request.
206
        $page->execute();
207
    }
208
209
    /**
210
     * Any cleanup tasks should go here
211
     *
212
     * Note that we need to be very careful here, as exceptions may have been thrown and handled.
213
     * This should *only* be for cleaning up, no logic should go here.
214
     */
215
    protected function cleanupEnvironment()
216
    {
217
        // Clean up anything we splurged after sending the page.
218
        if (ob_get_level() > 0) {
219
            for ($i = ob_get_level(); $i > 0; $i--) {
220
                ob_end_clean();
221
            }
222
        }
223
    }
224
225
    private function checkForceLogout()
226
    {
227
        $database = PdoDatabase::getDatabaseConnection($this->getConfiguration());
228
229
        $sessionUserId = WebRequest::getSessionUserId();
230
        iF ($sessionUserId === null) {
231
            return;
232
        }
233
234
        // Note, User::getCurrent() caches it's result, which we *really* don't want to trigger.
235
        $currentUser = User::getById($sessionUserId, $database);
236
237
        if ($currentUser === false) {
0 ignored issues
show
The condition $currentUser === false is always false.
Loading history...
238
            // Umm... this user has a session cookie with a userId set, but no user exists...
239
            Session::restart();
240
241
            $currentUser = User::getCurrent($database);
242
        }
243
244
        if ($currentUser->getForceLogout()) {
245
            Session::restart();
246
247
            $currentUser->setForceLogout(false);
248
            $currentUser->save();
249
        }
250
    }
251
252
    public function isPublic(): bool
253
    {
254
        return $this->isPublic;
255
    }
256
257
    public function setPublic(bool $isPublic): void
258
    {
259
        $this->isPublic = $isPublic;
260
    }
261
}
262