aboutsummaryrefslogtreecommitdiffhomepage
path: root/application/front
diff options
context:
space:
mode:
Diffstat (limited to 'application/front')
-rw-r--r--application/front/ShaarliMiddleware.php6
-rw-r--r--application/front/controller/admin/LogoutController.php10
-rw-r--r--application/front/controller/visitor/InstallController.php173
-rw-r--r--application/front/exceptions/AlreadyInstalledException.php15
-rw-r--r--application/front/exceptions/ResourcePermissionException.php13
5 files changed, 214 insertions, 3 deletions
diff --git a/application/front/ShaarliMiddleware.php b/application/front/ShaarliMiddleware.php
index baea6ef2..595182ac 100644
--- a/application/front/ShaarliMiddleware.php
+++ b/application/front/ShaarliMiddleware.php
@@ -43,6 +43,12 @@ class ShaarliMiddleware
43 $this->container->basePath = rtrim($request->getUri()->getBasePath(), '/'); 43 $this->container->basePath = rtrim($request->getUri()->getBasePath(), '/');
44 44
45 try { 45 try {
46 if (!is_file($this->container->conf->getConfigFileExt())
47 && !in_array($next->getName(), ['displayInstall', 'saveInstall'], true)
48 ) {
49 return $response->withRedirect($this->container->basePath . '/install');
50 }
51
46 $this->runUpdates(); 52 $this->runUpdates();
47 $this->checkOpenShaarli($request, $response, $next); 53 $this->checkOpenShaarli($request, $response, $next);
48 54
diff --git a/application/front/controller/admin/LogoutController.php b/application/front/controller/admin/LogoutController.php
index c5984814..28165129 100644
--- a/application/front/controller/admin/LogoutController.php
+++ b/application/front/controller/admin/LogoutController.php
@@ -4,6 +4,7 @@ declare(strict_types=1);
4 4
5namespace Shaarli\Front\Controller\Admin; 5namespace Shaarli\Front\Controller\Admin;
6 6
7use Shaarli\Security\CookieManager;
7use Shaarli\Security\LoginManager; 8use Shaarli\Security\LoginManager;
8use Slim\Http\Request; 9use Slim\Http\Request;
9use Slim\Http\Response; 10use Slim\Http\Response;
@@ -20,9 +21,12 @@ class LogoutController extends ShaarliAdminController
20 { 21 {
21 $this->container->pageCacheManager->invalidateCaches(); 22 $this->container->pageCacheManager->invalidateCaches();
22 $this->container->sessionManager->logout(); 23 $this->container->sessionManager->logout();
23 24 $this->container->cookieManager->setCookieParameter(
24 // TODO: switch to a simple Cookie manager allowing to check the session, and create mocks. 25 CookieManager::STAY_SIGNED_IN,
25 setcookie(LoginManager::$STAY_SIGNED_IN_COOKIE, 'false', 0, $this->container->basePath . '/'); 26 'false',
27 0,
28 $this->container->basePath . '/'
29 );
26 30
27 return $this->redirect($response, '/'); 31 return $this->redirect($response, '/');
28 } 32 }
diff --git a/application/front/controller/visitor/InstallController.php b/application/front/controller/visitor/InstallController.php
new file mode 100644
index 00000000..aa032860
--- /dev/null
+++ b/application/front/controller/visitor/InstallController.php
@@ -0,0 +1,173 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller\Visitor;
6
7use Shaarli\ApplicationUtils;
8use Shaarli\Bookmark\BookmarkFilter;
9use Shaarli\Container\ShaarliContainer;
10use Shaarli\Front\Exception\AlreadyInstalledException;
11use Shaarli\Front\Exception\ResourcePermissionException;
12use Shaarli\Languages;
13use Shaarli\Security\SessionManager;
14use Slim\Http\Request;
15use Slim\Http\Response;
16
17/**
18 * Slim controller used to render install page, and create initial configuration file.
19 */
20class InstallController extends ShaarliVisitorController
21{
22 public const SESSION_TEST_KEY = 'session_tested';
23 public const SESSION_TEST_VALUE = 'Working';
24
25 public function __construct(ShaarliContainer $container)
26 {
27 parent::__construct($container);
28
29 if (is_file($this->container->conf->getConfigFileExt())) {
30 throw new AlreadyInstalledException();
31 }
32 }
33
34 /**
35 * Display the install template page.
36 * Also test file permissions and sessions beforehand.
37 */
38 public function index(Request $request, Response $response): Response
39 {
40 // Before installation, we'll make sure that permissions are set properly, and sessions are working.
41 $this->checkPermissions();
42
43 if (static::SESSION_TEST_VALUE
44 !== $this->container->sessionManager->getSessionParameter(static::SESSION_TEST_KEY)
45 ) {
46 $this->container->sessionManager->setSessionParameter(static::SESSION_TEST_KEY, static::SESSION_TEST_VALUE);
47
48 return $this->redirect($response, '/install/session-test');
49 }
50
51 [$continents, $cities] = generateTimeZoneData(timezone_identifiers_list(), date_default_timezone_get());
52
53 $this->assignView('continents', $continents);
54 $this->assignView('cities', $cities);
55 $this->assignView('languages', Languages::getAvailableLanguages());
56
57 return $response->write($this->render('install'));
58 }
59
60 /**
61 * Route checking that the session parameter has been properly saved between two distinct requests.
62 * If the session parameter is preserved, redirect to install template page, otherwise displays error.
63 */
64 public function sessionTest(Request $request, Response $response): Response
65 {
66 // This part makes sure sessions works correctly.
67 // (Because on some hosts, session.save_path may not be set correctly,
68 // or we may not have write access to it.)
69 if (static::SESSION_TEST_VALUE
70 !== $this->container->sessionManager->getSessionParameter(static::SESSION_TEST_KEY)
71 ) {
72 // Step 2: Check if data in session is correct.
73 $msg = t(
74 '<pre>Sessions do not seem to work correctly on your server.<br>'.
75 'Make sure the variable "session.save_path" is set correctly in your PHP config, '.
76 'and that you have write access to it.<br>'.
77 'It currently points to %s.<br>'.
78 'On some browsers, accessing your server via a hostname like \'localhost\' '.
79 'or any custom hostname without a dot causes cookie storage to fail. '.
80 'We recommend accessing your server via it\'s IP address or Fully Qualified Domain Name.<br>'
81 );
82 $msg = sprintf($msg, $this->container->sessionManager->getSavePath());
83
84 $this->assignView('message', $msg);
85
86 return $response->write($this->render('error'));
87 }
88
89 return $this->redirect($response, '/install');
90 }
91
92 /**
93 * Save installation form and initialize config file and datastore if necessary.
94 */
95 public function save(Request $request, Response $response): Response
96 {
97 $timezone = 'UTC';
98 if (!empty($request->getParam('continent'))
99 && !empty($request->getParam('city'))
100 && isTimeZoneValid($request->getParam('continent'), $request->getParam('city'))
101 ) {
102 $timezone = $request->getParam('continent') . '/' . $request->getParam('city');
103 }
104 $this->container->conf->set('general.timezone', $timezone);
105
106 $login = $request->getParam('setlogin');
107 $this->container->conf->set('credentials.login', $login);
108 $salt = sha1(uniqid('', true) .'_'. mt_rand());
109 $this->container->conf->set('credentials.salt', $salt);
110 $this->container->conf->set('credentials.hash', sha1($request->getParam('setpassword') . $login . $salt));
111
112 if (!empty($request->getParam('title'))) {
113 $this->container->conf->set('general.title', escape($request->getParam('title')));
114 } else {
115 $this->container->conf->set(
116 'general.title',
117 'Shared bookmarks on '.escape(index_url($this->container->environment))
118 );
119 }
120
121 $this->container->conf->set('translation.language', escape($request->getParam('language')));
122 $this->container->conf->set('updates.check_updates', !empty($request->getParam('updateCheck')));
123 $this->container->conf->set('api.enabled', !empty($request->getParam('enableApi')));
124 $this->container->conf->set(
125 'api.secret',
126 generate_api_secret(
127 $this->container->conf->get('credentials.login'),
128 $this->container->conf->get('credentials.salt')
129 )
130 );
131
132 try {
133 // Everything is ok, let's create config file.
134 $this->container->conf->write($this->container->loginManager->isLoggedIn());
135 } catch (\Exception $e) {
136 $this->assignView('message', $e->getMessage());
137 $this->assignView('stacktrace', $e->getTraceAsString());
138
139 return $response->write($this->render('error'));
140 }
141
142 if ($this->container->bookmarkService->count(BookmarkFilter::$ALL) === 0) {
143 $this->container->bookmarkService->initialize();
144 }
145
146 $this->container->sessionManager->setSessionParameter(
147 SessionManager::KEY_SUCCESS_MESSAGES,
148 [t('Shaarli is now configured. Please login and start shaaring your bookmarks!')]
149 );
150
151 return $this->redirect($response, '/');
152 }
153
154 protected function checkPermissions(): bool
155 {
156 // Ensure Shaarli has proper access to its resources
157 $errors = ApplicationUtils::checkResourcePermissions($this->container->conf);
158
159 if (empty($errors)) {
160 return true;
161 }
162
163 // FIXME! Do not insert HTML here.
164 $message = '<p>'. t('Insufficient permissions:') .'</p><ul>';
165
166 foreach ($errors as $error) {
167 $message .= '<li>'.$error.'</li>';
168 }
169 $message .= '</ul>';
170
171 throw new ResourcePermissionException($message);
172 }
173}
diff --git a/application/front/exceptions/AlreadyInstalledException.php b/application/front/exceptions/AlreadyInstalledException.php
new file mode 100644
index 00000000..4add86cf
--- /dev/null
+++ b/application/front/exceptions/AlreadyInstalledException.php
@@ -0,0 +1,15 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Exception;
6
7class AlreadyInstalledException extends ShaarliFrontException
8{
9 public function __construct()
10 {
11 $message = t('Shaarli has already been installed. Login to edit the configuration.');
12
13 parent::__construct($message, 401);
14 }
15}
diff --git a/application/front/exceptions/ResourcePermissionException.php b/application/front/exceptions/ResourcePermissionException.php
new file mode 100644
index 00000000..8fbf03b9
--- /dev/null
+++ b/application/front/exceptions/ResourcePermissionException.php
@@ -0,0 +1,13 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Exception;
6
7class ResourcePermissionException extends ShaarliFrontException
8{
9 public function __construct(string $message)
10 {
11 parent::__construct($message, 500);
12 }
13}