Completed
Push — master ( 12ea5b...b9b773 )
by Gabor
03:26
created

EnvironmentManager::secureSession()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 17
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 17
c 0
b 0
f 0
ccs 0
cts 0
cp 0
rs 9.4285
cc 1
eloc 11
nc 1
nop 0
crap 2
1
<?php
2
/**
3
 * WebHemi.
4
 *
5
 * PHP version 5.6
6
 *
7
 * @copyright 2012 - 2016 Gixx-web (http://www.gixx-web.com)
8
 * @license   https://opensource.org/licenses/MIT The MIT License (MIT)
9
 *
10
 * @link      http://www.gixx-web.com
11
 */
12
namespace WebHemi\Application;
13
14
use InvalidArgumentException;
15
use WebHemi\Config\ConfigInterface;
16
17
/**
18
 * Class EnvironmentManager.
19
 */
20
class EnvironmentManager
21
{
22
    const APPLICATION_TYPE_DIRECTORY = 'directory';
23
    const APPLICATION_TYPE_DOMAIN = 'domain';
24
25
    const COOKIE_AUTO_LOGIN_PREFIX = 'atln';
26
    const COOKIE_SESSION_PREFIX = 'atsn';
27
28
    const DEFAULT_APPLICATION = 'website';
29
    const DEFAULT_APPLICATION_URI = '/';
30
    const DEFAULT_MODULE = 'Website';
31
    const DEFAULT_THEME = 'default';
32
    const DEFAULT_THEME_RESOURCE_PATH = '/resources/default_theme';
33
34
    const SESSION_SALT = 'WebHemi';
35
36
    /** @var ConfigInterface */
37
    private $config;
38
    /** @var string */
39
    private $url;
40
    /** @var string */
41
    private $subDomain;
42
    /** @var string */
43
    private $mainDomain;
44
    /** @var string */
45
    private $applicationDomain;
46
    /** @var string */
47
    private $documentRoot;
48
    /** @var string */
49
    private $selectedModule;
50
    /** @var string */
51
    private $selectedApplication;
52
    /** @var string */
53
    private $selectedApplicationUri;
54
    /** @var string */
55
    private $selectedTheme;
56
    /** @var string */
57
    private $selectedThemeResourcePath;
58
    /** @var array  */
59
    private $environmentData;
60
61
    /**
62
     * ModuleManager constructor.
63
     *
64
     * @param ConfigInterface $config
65
     * @param array           $getData
66
     * @param array           $postData
67
     * @param array           $serverData
68
     * @param array           $cookieData
69
     * @param array           $filesData
70
     */
71 9
    public function __construct(
72
        ConfigInterface $config,
73
        array $getData,
74
        array $postData,
75
        array $serverData,
76
        array $cookieData,
77
        array $filesData
78
    ) {
79 9
        $this->config = $config;
80 9
        $this->documentRoot = realpath(__DIR__.'/../../../');
81
82 9
        $this->environmentData = [
83 9
            'GET'    => $getData,
84 9
            'POST'   => $postData,
85 9
            'SERVER' => $serverData,
86 9
            'COOKIE' => $cookieData,
87 9
            'FILES'  => $filesData,
88
        ];
89
90 9
        $isHttps = isset($this->environmentData['SERVER']['HTTPS']) && $this->environmentData['SERVER']['HTTPS'];
91 9
        $this->url = 'http'.($isHttps ? 's' : '').'://'
92 9
            .$this->environmentData['SERVER']['HTTP_HOST']
93 9
            .$this->environmentData['SERVER']['REQUEST_URI']
94 9
            .$this->environmentData['SERVER']['QUERY_STRING'];
95
96 9
        $this->selectedModule = self::DEFAULT_MODULE;
97 9
        $this->selectedApplication = self::DEFAULT_APPLICATION;
98 9
        $this->selectedTheme = self::DEFAULT_THEME;
99 9
        $this->selectedThemeResourcePath = self::DEFAULT_THEME_RESOURCE_PATH;
100 9
        $this->selectedApplicationUri = self::DEFAULT_APPLICATION_URI;
101
102 9
        $this->secureSession()
103 9
            ->setDomain()
104 9
            ->selectModuleApplicationAndTheme();
105 9
    }
106
107
    /**
108
     * Gets the selected application.
109
     *
110
     * @return string
111
     */
112 4
    public function getSelectedApplication()
113
    {
114 4
        return $this->selectedApplication;
115
    }
116
117
    /**
118
     * Get the URI path for the selected application. Required for the RouterAdapter to work with directory-based
119
     * applications correctly.
120
     *
121
     * @return string
122
     */
123 8
    public function getSelectedApplicationUri()
124
    {
125 8
        return $this->selectedApplicationUri;
126
    }
127
128
    /**
129
     * Gets the selected module.
130
     *
131
     * @return string
132
     */
133 5
    public function getSelectedModule()
134
    {
135 5
        return $this->selectedModule;
136
    }
137
138
    /**
139
     * Gets the selected theme.
140
     *
141
     * @return string
142
     */
143 5
    public function getSelectedTheme()
144
    {
145 5
        return $this->selectedTheme;
146
    }
147
148
    /**
149
     * Gets the resource path for the selected theme.
150
     *
151
     * @return string
152
     */
153 6
    public function getResourcePath()
154
    {
155 6
        return $this->selectedThemeResourcePath;
156
    }
157
158
    /**
159
     * Gets environment data.
160
     *
161
     * @param string $key
162
     *
163
     * @return array
164
     */
165 5
    public function getEnvironmentData($key)
166
    {
167 5
        if (!isset($this->environmentData[$key])) {
168 1
            throw new InvalidArgumentException(sprintf('The "%s" is not a valid environment key.', $key));
169
        }
170
171 5
        return $this->environmentData[$key];
172
    }
173
174
    /**
175
     * Gets the template settings for a specific theme.
176
     *
177
     * @param string $theme
178
     *
179
     * @codeCoverageIgnore - @see \WebHemiTest\Config\ConfigTest
180
     *
181
     * @return ConfigInterface
182
     */
183
    public function getApplicationTemplateSettings($theme = self::DEFAULT_THEME)
184
    {
185
        return $this->config->getConfig('themes/'.$theme);
186
    }
187
188
    /**
189
     * Gets the routing settings for the selected module.
190
     *
191
     * @codeCoverageIgnore - @see \WebHemiTest\Config\ConfigTest
192
     *
193
     * @return ConfigInterface
194
     */
195
    public function getModuleRouteSettings()
196
    {
197
        return $this->config->getConfig('modules/'.$this->getSelectedModule().'/routing');
198
    }
199
200
    /**
201
     * Overwrite PHP settings to be more secure
202
     *
203
     * @codeCoverageIgnore - Core functions.
204
     *
205
     * @return $this
206
     */
207
    private function secureSession()
208
    {
209
        ini_set('session.entropy_file', '/dev/urandom');
210
        ini_set('session.entropy_length', '16');
211
        ini_set('session.hash_function', 'sha256');
212
        ini_set('session.use_only_cookies', '1');
213
        ini_set('session.use_cookies', '1');
214
        ini_set('session.use_trans_sid', '0');
215
        ini_set('session.cookie_httponly', '1');
216
217
        // hide session name
218
        session_name(self::COOKIE_SESSION_PREFIX.'-'.bin2hex(self::SESSION_SALT));
219
        // set session lifetime to 1 hour
220
        session_set_cookie_params(3600);
221
222
        return $this;
223
    }
224
225
    /**
226
     * Parses server data and tries to set domain information.
227
     *
228
     * @return $this
229
     */
230 9
    private function setDomain()
231
    {
232 9
        $domain = $this->environmentData['SERVER']['SERVER_NAME'];
233 9
        $subDomain = '';
234 9
        $urlParts = parse_url($this->url);
235
236
        // If the host is not an IP address, then check the sub-domain-based module names too
237 9
        if (!preg_match(
238 9
            '/^((\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])\.){3}(\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])$/',
239 9
            $urlParts['host']
240 9
        )) {
241 9
            $domainParts = explode('.', $urlParts['host']);
242
            // @todo find out how to support complex TLDs like `.co.uk` or `.com.br`
243 9
            $tld = array_pop($domainParts);
244 9
            $domain = array_pop($domainParts).'.'.$tld;
245
            // the rest is the sub-domain
246 9
            $subDomain = implode('.', $domainParts);
247 9
        }
248
249
        // If no sub-domain presents, then it should be handled as 'www'
250 9
        if (empty($subDomain)) {
251 7
            $subDomain = 'www';
252 7
        }
253
254 9
        $this->subDomain = $subDomain;
255 9
        $this->mainDomain = $domain;
256 9
        $this->applicationDomain = $this->subDomain.'.'. $this->mainDomain;
257
258 9
        return $this;
259
    }
260
261
    /**
262
     * From the parsed domain data, selects the application, module and theme.
263
     *
264
     * @return $this
265
     */
266 9
    private function selectModuleApplicationAndTheme()
267
    {
268 9
        $urlParts = parse_url($this->url);
269 9
        $applications = $this->config->getData('applications');
270
271
        // Only the first segment is important (if exists).
272 9
        list($subDirectory) = explode('/', ltrim($urlParts['path'], '/'), 2);
273
274
        $applicationDataFixture = [
275 9
            'type' => self::APPLICATION_TYPE_DIRECTORY,
276 9
            'module' => self::DEFAULT_MODULE,
277 9
            'theme' => self::DEFAULT_THEME,
278 9
        ];
279
280
        // Run through the available application-modules to validate and find active module
281 9
        foreach ($applications as $applicationName => $applicationData) {
282
            // Don't risk, fix.
283 9
            $applicationData = array_merge($applicationDataFixture, $applicationData);
284
285 9
            if ($this->checkDirectoryIsValid($applicationName, $applicationData, $subDirectory)
286 9
                || $this->checkDomainIsValid($applicationName, $applicationData, $subDirectory)
287 9
            ) {
288 3
                $this->selectedModule = $applicationData['module'];
289 3
                $this->selectedApplication = (string)$applicationName;
290 3
                $this->selectedTheme = $applicationData['theme'];
291
292 3
                $this->selectedApplicationUri = '/'.$subDirectory;
293 3
                break;
294
            }
295 9
        }
296
297 9
        if ($this->selectedTheme !== self::DEFAULT_THEME) {
298 1
            $this->selectedThemeResourcePath = '/resources/vendor_themes/'.$this->selectedTheme;
299 1
        }
300
301 9
        return $this;
302
    }
303
304
    /**
305
     * Checks from type, path it the current URI segment is valid.
306
     *
307
     * @param string $applicationName
308
     * @param array  $applicationData
309
     * @param string $subDirectory
310
     *
311
     * @return bool
312
     */
313 9
    private function checkDirectoryIsValid($applicationName, $applicationData, $subDirectory)
314
    {
315 9
        return $this->subDomain == 'www'
316 9
            && $applicationName != 'website'
317 9
            && !empty($subDirectory)
318 9
            && $applicationData['type'] == self::APPLICATION_TYPE_DIRECTORY
319 9
            && $applicationData['path'] == $subDirectory;
320
    }
321
322
    /**
323
     * Checks from type and path if the domain is valid. If so, it sets the $subDirectory to the default.
324
     *
325
     * @param string $applicationName
326
     * @param array  $applicationData
327
     * @param string $subDirectory
328
     *
329
     * @return bool
330
     */
331 9
    private function checkDomainIsValid($applicationName, $applicationData, &$subDirectory)
332
    {
333
        $isSubdomain = $applicationName == 'website'
334 9
            || (
335 9
                $this->subDomain != 'www'
336 9
                && $applicationData['type'] == self::APPLICATION_TYPE_DOMAIN
337 9
                && $applicationData['path'] == $this->subDomain
338 9
            );
339
340
        // If this method get called and will return TRUE, it means the $subDirectory paramtere will be used only for
341
        // setting the right selectedApplicationUri. To avoid complexity, we change it here. Doesn't matter.
342 9
        if ($isSubdomain) {
343 2
            $subDirectory = '';
344 2
        }
345
346 9
        return $isSubdomain;
347
    }
348
}
349