diff options
Diffstat (limited to 'application/front')
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 | ||
5 | namespace Shaarli\Front\Controller\Admin; | 5 | namespace Shaarli\Front\Controller\Admin; |
6 | 6 | ||
7 | use Shaarli\Security\CookieManager; | ||
7 | use Shaarli\Security\LoginManager; | 8 | use Shaarli\Security\LoginManager; |
8 | use Slim\Http\Request; | 9 | use Slim\Http\Request; |
9 | use Slim\Http\Response; | 10 | use 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 | |||
3 | declare(strict_types=1); | ||
4 | |||
5 | namespace Shaarli\Front\Controller\Visitor; | ||
6 | |||
7 | use Shaarli\ApplicationUtils; | ||
8 | use Shaarli\Bookmark\BookmarkFilter; | ||
9 | use Shaarli\Container\ShaarliContainer; | ||
10 | use Shaarli\Front\Exception\AlreadyInstalledException; | ||
11 | use Shaarli\Front\Exception\ResourcePermissionException; | ||
12 | use Shaarli\Languages; | ||
13 | use Shaarli\Security\SessionManager; | ||
14 | use Slim\Http\Request; | ||
15 | use Slim\Http\Response; | ||
16 | |||
17 | /** | ||
18 | * Slim controller used to render install page, and create initial configuration file. | ||
19 | */ | ||
20 | class 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 | |||
3 | declare(strict_types=1); | ||
4 | |||
5 | namespace Shaarli\Front\Exception; | ||
6 | |||
7 | class 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 | |||
3 | declare(strict_types=1); | ||
4 | |||
5 | namespace Shaarli\Front\Exception; | ||
6 | |||
7 | class ResourcePermissionException extends ShaarliFrontException | ||
8 | { | ||
9 | public function __construct(string $message) | ||
10 | { | ||
11 | parent::__construct($message, 500); | ||
12 | } | ||
13 | } | ||