1
|
|
|
<?php |
2
|
|
|
namespace library\components { |
3
|
|
|
|
4
|
|
|
use library\components\cms\ConfigurationRouting; |
5
|
|
|
use library\components\cms\DocumentRouting; |
6
|
|
|
use library\components\cms\FilesRouting; |
7
|
|
|
use library\components\cms\ImagesRouting; |
8
|
|
|
use library\components\cms\SearchRouting; |
9
|
|
|
use library\components\cms\SitemapRouting; |
10
|
|
|
use library\crypt\Crypt; |
11
|
|
|
use library\storage\Storage; |
12
|
|
|
|
13
|
|
|
class CmsComponent extends BaseComponent |
14
|
|
|
{ |
15
|
|
|
/** |
16
|
|
|
* @var \library\storage\Storage |
17
|
|
|
*/ |
18
|
|
|
public $storage; |
19
|
|
|
|
20
|
|
|
const INVALID_CREDENTIALS_MESSAGE = 'Invalid username / password combination'; |
21
|
|
|
|
22
|
|
|
const MAIN_NAV_CLASS = 'default'; |
23
|
|
|
|
24
|
|
|
const PARAMETER_APPLICATION_COMPONENT = 'applicationComponent'; |
25
|
|
|
const PARAMETER_APPLICATION_COMPONENTS = 'applicationComponents'; |
26
|
|
|
const PARAMETER_BLACKLIST_IPS = 'blacklistIps'; |
27
|
|
|
const PARAMETER_BODY = 'body'; |
28
|
|
|
const PARAMETER_BRICK = 'brick'; |
29
|
|
|
const PARAMETER_BRICKS = 'bricks'; |
30
|
|
|
const PARAMETER_CMS_PREFIX = 'cmsPrefix'; |
31
|
|
|
const PARAMETER_CONFIGURATION = 'configuration'; |
32
|
|
|
const PARAMETER_DOCUMENT = 'document'; |
33
|
|
|
const PARAMETER_DOCUMENTS = 'documents'; |
34
|
|
|
const PARAMETER_DOCUMENT_TYPE = 'documentType'; |
35
|
|
|
const PARAMETER_DOCUMENT_TYPES = 'documentTypes'; |
36
|
|
|
const PARAMETER_ERROR_MESSAGE = 'errorMsg'; |
37
|
|
|
const PARAMETER_FILES = 'files'; |
38
|
|
|
const PARAMETER_FOLDER = 'folder'; |
39
|
|
|
const PARAMETER_IMAGE = 'image'; |
40
|
|
|
const PARAMETER_IMAGES = 'images'; |
41
|
|
|
const PARAMETER_IMAGE_SET = 'imageSet'; |
42
|
|
|
const PARAMETER_MAIN_NAV_CLASS = 'mainNavClass'; |
43
|
|
|
const PARAMETER_MY_BRICK_SLUG = 'myBrickSlug'; |
44
|
|
|
const PARAMETER_REDIRECT = 'redirect'; |
45
|
|
|
const PARAMETER_REDIRECTS = 'redirects'; |
46
|
|
|
const PARAMETER_SEARCH = 'search'; |
47
|
|
|
const PARAMETER_SEARCH_LOG = "searchLog"; |
48
|
|
|
const PARAMETER_SEARCH_NEEDS_UPDATE = "searchNeedsUpdate"; |
49
|
|
|
const PARAMETER_SITEMAP = 'sitemap'; |
50
|
|
|
const PARAMETER_SITEMAP_ITEM = 'sitemapItem'; |
51
|
|
|
const PARAMETER_SMALLEST_IMAGE = 'smallestImage'; |
52
|
|
|
const PARAMETER_STATIC = 'static'; |
53
|
|
|
const PARAMETER_USER = 'user'; |
54
|
|
|
const PARAMETER_USERS = 'users'; |
55
|
|
|
const PARAMETER_USER_RIGHTS = 'userRights'; |
56
|
|
|
const PARAMETER_VALUELIST = "valuelist"; |
57
|
|
|
const PARAMETER_VALUELISTS = "valuelists"; |
58
|
|
|
const PARAMETER_WHITELIST_IPS = 'whitelistIps'; |
59
|
|
|
|
60
|
|
|
const POST_PARAMETER_COMPONENT = 'component'; |
61
|
|
|
const POST_PARAMETER_FROM_URL = "fromUrl"; |
62
|
|
|
const POST_PARAMETER_PASSWORD = 'password'; |
63
|
|
|
const POST_PARAMETER_SAVE = 'save'; |
64
|
|
|
const POST_PARAMETER_TEMPLATE = 'template'; |
65
|
|
|
const POST_PARAMETER_TITLE = 'title'; |
66
|
|
|
const POST_PARAMETER_TO_URL = "toUrl"; |
67
|
|
|
const POST_PARAMETER_USERNAME = 'username'; |
68
|
|
|
|
69
|
|
|
const GET_PARAMETER_PATH = 'path'; |
70
|
|
|
const GET_PARAMETER_SLUG = 'slug'; |
71
|
|
|
|
72
|
|
|
const FILES_PARAMETER_FILE = 'file'; |
73
|
|
|
|
74
|
|
|
const SESSION_PARAMETER_CLOUD_CONTROL = 'cloudcontrol'; |
75
|
|
|
|
76
|
|
|
const LOGIN_TEMPLATE_PATH = 'cms/login'; |
77
|
|
|
|
78
|
|
|
const CONTENT_TYPE_APPLICATION_JSON = 'Content-type:application/json'; |
79
|
|
|
|
80
|
|
|
public $subTemplate = null; |
81
|
|
|
|
82
|
|
|
|
83
|
|
|
/** |
84
|
|
|
* @param Storage $storage |
85
|
|
|
* |
86
|
|
|
* @return void |
87
|
|
|
*/ |
88
|
|
|
public function run(Storage $storage) |
89
|
|
|
{ |
90
|
|
|
$this->parameters[self::PARAMETER_MAIN_NAV_CLASS] = self::MAIN_NAV_CLASS; |
91
|
|
|
$this->storage = $storage; |
92
|
|
|
|
93
|
|
|
$remoteAddress = $_SERVER['REMOTE_ADDR']; |
94
|
|
|
$this->checkWhiteList($remoteAddress); |
95
|
|
|
$this->checkBlackList($remoteAddress); |
96
|
|
|
|
97
|
|
|
$this->checkLogin(); |
98
|
|
|
|
99
|
|
|
$this->parameters[self::PARAMETER_USER_RIGHTS] = $_SESSION[self::SESSION_PARAMETER_CLOUD_CONTROL]->rights; |
100
|
|
|
|
101
|
|
|
$this->routing(); |
102
|
|
|
} |
103
|
|
|
|
104
|
|
|
/** |
105
|
|
|
* See if a user is logged or wants to log in and |
106
|
|
|
* takes appropriate actions. |
107
|
|
|
* |
108
|
|
|
* @throws \Exception |
109
|
|
|
*/ |
110
|
|
|
protected function checkLogin() |
111
|
|
|
{ |
112
|
|
|
$request = $this->request; |
113
|
|
|
|
114
|
|
|
if (!isset($_SESSION[self::SESSION_PARAMETER_CLOUD_CONTROL])) { |
115
|
|
|
if (isset($request::$post[self::POST_PARAMETER_USERNAME], $request::$post[self::POST_PARAMETER_PASSWORD])) { |
116
|
|
|
$this->checkLoginAttempt($request); |
117
|
|
|
} else { |
118
|
|
|
$this->showLogin(); |
119
|
|
|
} |
120
|
|
|
} |
121
|
|
|
} |
122
|
|
|
|
123
|
|
|
/** |
124
|
|
|
* Overrides normal behaviour and only renders the |
125
|
|
|
* login screen |
126
|
|
|
* |
127
|
|
|
* @throws \Exception |
128
|
|
|
*/ |
129
|
|
|
protected function showLogin() |
130
|
|
|
{ |
131
|
|
|
$loginTemplatePath = self::LOGIN_TEMPLATE_PATH; |
132
|
|
|
$this->renderTemplate($loginTemplatePath); |
133
|
|
|
ob_end_flush(); |
134
|
|
|
exit; |
135
|
|
|
} |
136
|
|
|
|
137
|
|
|
/** |
138
|
|
|
* As an exception, to keep the initial file structure simple |
139
|
|
|
* the cms implements it's own routing, apart from the regular sitemap functionality |
140
|
|
|
* |
141
|
|
|
* @throws \Exception |
142
|
|
|
*/ |
143
|
|
|
protected function routing() |
144
|
|
|
{ |
145
|
|
|
$relativeCmsUri = $this->getRelativeCmsUri($this->request); |
146
|
|
|
|
147
|
|
|
$userRights = $_SESSION[self::SESSION_PARAMETER_CLOUD_CONTROL]->rights; |
148
|
|
|
|
149
|
|
|
$this->dashboardRouting($relativeCmsUri); |
150
|
|
|
$this->logOffRouting($this->request, $relativeCmsUri); |
151
|
|
|
$this->apiRouting($relativeCmsUri); |
152
|
|
|
$this->documentRouting($userRights, $relativeCmsUri); |
153
|
|
|
$this->sitemapRouting($userRights, $relativeCmsUri); |
154
|
|
|
$this->imageRouting($userRights, $relativeCmsUri); |
155
|
|
|
$this->filesRouting($userRights, $relativeCmsUri); |
156
|
|
|
$this->configurationRouting($userRights, $relativeCmsUri); |
157
|
|
|
$this->searchRouting($userRights, $relativeCmsUri); |
158
|
|
|
|
159
|
|
|
$this->renderBody(); |
160
|
|
|
} |
161
|
|
|
|
162
|
|
|
/** |
163
|
|
|
* @param $remoteAddress |
164
|
|
|
* |
165
|
|
|
* @throws \Exception |
166
|
|
|
*/ |
167
|
|
View Code Duplication |
private function checkWhiteList($remoteAddress) |
|
|
|
|
168
|
|
|
{ |
169
|
|
|
if (isset($this->parameters[self::PARAMETER_WHITELIST_IPS])) { |
170
|
|
|
$whitelistIps = explode(',', $this->parameters[self::PARAMETER_WHITELIST_IPS]); |
171
|
|
|
$whitelistIps = array_map("trim", $whitelistIps); |
172
|
|
|
if (!in_array($remoteAddress, $whitelistIps)) { |
173
|
|
|
throw new \Exception('Ip address ' . $remoteAddress . ' is not on whitelist'); |
174
|
|
|
} |
175
|
|
|
} |
176
|
|
|
} |
177
|
|
|
|
178
|
|
|
/** |
179
|
|
|
* @param $remoteAddress |
180
|
|
|
* |
181
|
|
|
* @throws \Exception |
182
|
|
|
*/ |
183
|
|
View Code Duplication |
private function checkBlackList($remoteAddress) |
|
|
|
|
184
|
|
|
{ |
185
|
|
|
if (isset($this->parameters[self::PARAMETER_BLACKLIST_IPS])) { |
186
|
|
|
$blacklistIps = explode(',', $this->parameters[self::PARAMETER_BLACKLIST_IPS]); |
187
|
|
|
$blacklistIps = array_map("trim", $blacklistIps); |
188
|
|
|
if (in_array($remoteAddress, $blacklistIps)) { |
189
|
|
|
throw new \Exception('Ip address ' . $remoteAddress . ' is on blacklist'); |
190
|
|
|
} |
191
|
|
|
} |
192
|
|
|
} |
193
|
|
|
|
194
|
|
|
/** |
195
|
|
|
* @param $request |
196
|
|
|
* |
197
|
|
|
* @return mixed|string |
198
|
|
|
*/ |
199
|
|
|
private function getRelativeCmsUri($request) |
200
|
|
|
{ |
201
|
|
|
// TODO Use regex match parameter instead of calculating relative uri |
202
|
|
|
$pos = strpos($request::$relativeUri, $this->parameters[self::PARAMETER_CMS_PREFIX]); |
203
|
|
|
$relativeCmsUri = '/'; |
204
|
|
|
if ($pos !== false) { |
205
|
|
|
$relativeCmsUri = substr_replace($request::$relativeUri, '', $pos, strlen($this->parameters[self::PARAMETER_CMS_PREFIX])); |
206
|
|
|
} |
207
|
|
|
|
208
|
|
|
return $relativeCmsUri; |
209
|
|
|
} |
210
|
|
|
|
211
|
|
|
/** |
212
|
|
|
* @param $relativeCmsUri |
213
|
|
|
*/ |
214
|
|
|
private function apiRouting($relativeCmsUri) |
215
|
|
|
{ |
216
|
|
|
if ($relativeCmsUri == '/images.json') { |
217
|
|
|
header(self::CONTENT_TYPE_APPLICATION_JSON); |
218
|
|
|
die(json_encode($this->storage->getImages()->getImages())); |
219
|
|
|
} elseif ($relativeCmsUri == '/files.json') { |
220
|
|
|
header(self::CONTENT_TYPE_APPLICATION_JSON); |
221
|
|
|
die(json_encode($this->storage->getFiles()->getFiles())); |
222
|
|
|
} elseif ($relativeCmsUri == '/documents.json') { |
223
|
|
|
header(self::CONTENT_TYPE_APPLICATION_JSON); |
224
|
|
|
die(json_encode($this->storage->getDocuments()->getDocuments())); |
225
|
|
|
} |
226
|
|
|
} |
227
|
|
|
|
228
|
|
|
private function logOffRouting($request, $relativeCmsUri) |
229
|
|
|
{ |
230
|
|
|
if ($relativeCmsUri == '/log-off') { |
231
|
|
|
$_SESSION[self::SESSION_PARAMETER_CLOUD_CONTROL] = null; |
232
|
|
|
unset($_SESSION[self::SESSION_PARAMETER_CLOUD_CONTROL]); |
233
|
|
|
header('Location: ' . $request::$subfolders . $this->parameters[self::PARAMETER_CMS_PREFIX]); |
234
|
|
|
exit; |
235
|
|
|
} |
236
|
|
|
} |
237
|
|
|
|
238
|
|
|
public function setParameter($parameterName, $parameterValue) |
239
|
|
|
{ |
240
|
|
|
$this->parameters[$parameterName] = $parameterValue; |
241
|
|
|
} |
242
|
|
|
|
243
|
|
|
public function getParameter($parameterName) |
244
|
|
|
{ |
245
|
|
|
return $this->parameters[$parameterName]; |
246
|
|
|
} |
247
|
|
|
|
248
|
|
|
/** |
249
|
|
|
* @param $relativeCmsUri |
250
|
|
|
*/ |
251
|
|
|
protected function dashboardRouting($relativeCmsUri) |
252
|
|
|
{ |
253
|
|
|
if ($relativeCmsUri == '' || $relativeCmsUri == '/') { |
254
|
|
|
$this->subTemplate = 'cms/dashboard'; |
255
|
|
|
} |
256
|
|
|
} |
257
|
|
|
|
258
|
|
|
/** |
259
|
|
|
* @param $userRights |
260
|
|
|
* @param $relativeCmsUri |
261
|
|
|
*/ |
262
|
|
|
protected function documentRouting($userRights, $relativeCmsUri) |
263
|
|
|
{ |
264
|
|
|
if (in_array(self::PARAMETER_DOCUMENTS, $userRights)) { |
265
|
|
|
new DocumentRouting($this->request, $relativeCmsUri, $this); |
266
|
|
|
} |
267
|
|
|
} |
268
|
|
|
|
269
|
|
|
/** |
270
|
|
|
* @param $userRights |
271
|
|
|
* @param $relativeCmsUri |
272
|
|
|
*/ |
273
|
|
|
protected function sitemapRouting($userRights, $relativeCmsUri) |
274
|
|
|
{ |
275
|
|
|
if (in_array(self::PARAMETER_SITEMAP, $userRights)) { |
276
|
|
|
new SitemapRouting($this->request, $relativeCmsUri, $this); |
277
|
|
|
} |
278
|
|
|
} |
279
|
|
|
|
280
|
|
|
/** |
281
|
|
|
* @param $userRights |
282
|
|
|
* @param $relativeCmsUri |
283
|
|
|
*/ |
284
|
|
|
protected function imageRouting($userRights, $relativeCmsUri) |
285
|
|
|
{ |
286
|
|
|
if (in_array(self::PARAMETER_IMAGES, $userRights)) { |
287
|
|
|
new ImagesRouting($this->request, $relativeCmsUri, $this); |
288
|
|
|
} |
289
|
|
|
} |
290
|
|
|
|
291
|
|
|
/** |
292
|
|
|
* @param $userRights |
293
|
|
|
* @param $relativeCmsUri |
294
|
|
|
*/ |
295
|
|
|
protected function filesRouting($userRights, $relativeCmsUri) |
296
|
|
|
{ |
297
|
|
|
if (in_array(self::PARAMETER_FILES, $userRights)) { |
298
|
|
|
new FilesRouting($this->request, $relativeCmsUri, $this); |
299
|
|
|
} |
300
|
|
|
} |
301
|
|
|
|
302
|
|
|
/** |
303
|
|
|
* @param $userRights |
304
|
|
|
* @param $relativeCmsUri |
305
|
|
|
*/ |
306
|
|
|
protected function configurationRouting($userRights, $relativeCmsUri) |
307
|
|
|
{ |
308
|
|
|
if (in_array('configuration', $userRights)) { |
309
|
|
|
new ConfigurationRouting($this->request, $relativeCmsUri, $this); |
310
|
|
|
} |
311
|
|
|
} |
312
|
|
|
|
313
|
|
|
protected function renderBody() |
314
|
|
|
{ |
315
|
|
|
if ($this->subTemplate !== null) { |
316
|
|
|
$this->parameters[self::PARAMETER_BODY] = $this->renderTemplate($this->subTemplate); |
317
|
|
|
} |
318
|
|
|
} |
319
|
|
|
|
320
|
|
|
/** |
321
|
|
|
* @param $crypt |
322
|
|
|
* @param $request |
323
|
|
|
*/ |
324
|
|
|
protected function invalidCredentials($crypt, $request) |
325
|
|
|
{ |
326
|
|
|
$crypt->encrypt($request::$post[self::POST_PARAMETER_PASSWORD], 16); // Buy time, to avoid brute forcing |
327
|
|
|
$this->parameters[self::PARAMETER_ERROR_MESSAGE] = self::INVALID_CREDENTIALS_MESSAGE; |
328
|
|
|
$this->showLogin(); |
329
|
|
|
} |
330
|
|
|
|
331
|
|
|
/** |
332
|
|
|
* @param $user |
333
|
|
|
* @param $crypt |
334
|
|
|
* @param $request |
335
|
|
|
*/ |
336
|
|
|
protected function checkPassword($user, $crypt, $request) |
337
|
|
|
{ |
338
|
|
|
$salt = $user->salt; |
339
|
|
|
$password = $user->password; |
340
|
|
|
|
341
|
|
|
$passwordCorrect = $crypt->compare($request::$post[self::POST_PARAMETER_PASSWORD], $password, $salt); |
342
|
|
|
|
343
|
|
|
if ($passwordCorrect) { |
344
|
|
|
$_SESSION[self::SESSION_PARAMETER_CLOUD_CONTROL] = $user; |
345
|
|
|
} else { |
346
|
|
|
$this->parameters[self::PARAMETER_ERROR_MESSAGE] = self::INVALID_CREDENTIALS_MESSAGE; |
347
|
|
|
$this->showLogin(); |
348
|
|
|
} |
349
|
|
|
} |
350
|
|
|
|
351
|
|
|
/** |
352
|
|
|
* @param $request |
353
|
|
|
*/ |
354
|
|
|
protected function checkLoginAttempt($request) |
355
|
|
|
{ |
356
|
|
|
$user = $this->storage->getUsers()->getUserByUsername($request::$post[self::POST_PARAMETER_USERNAME]); |
357
|
|
|
$crypt = new Crypt(); |
358
|
|
|
if (empty($user)) { |
359
|
|
|
$this->invalidCredentials($crypt, $request); |
360
|
|
|
} else { |
361
|
|
|
$this->checkPassword($user, $crypt, $request); |
362
|
|
|
} |
363
|
|
|
} |
364
|
|
|
|
365
|
|
|
private function searchRouting($userRights, $relativeCmsUri) |
366
|
|
|
{ |
367
|
|
|
if (in_array(self::PARAMETER_SEARCH, $userRights)) { |
368
|
|
|
new SearchRouting($this->request, $relativeCmsUri, $this); |
369
|
|
|
} |
370
|
|
|
} |
371
|
|
|
} |
372
|
|
|
} |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.