diff options
Diffstat (limited to 'application')
-rw-r--r-- | application/Utils.php | 2 | ||||
-rw-r--r-- | application/container/ContainerBuilder.php | 81 | ||||
-rw-r--r-- | application/container/ShaarliContainer.php | 30 | ||||
-rw-r--r-- | application/front/ShaarliMiddleware.php | 57 | ||||
-rw-r--r-- | application/front/controllers/LoginController.php | 48 | ||||
-rw-r--r-- | application/front/controllers/ShaarliController.php | 69 | ||||
-rw-r--r-- | application/front/exceptions/LoginBannedException.php | 15 | ||||
-rw-r--r-- | application/front/exceptions/ShaarliException.php | 23 | ||||
-rw-r--r-- | application/render/PageBuilder.php | 17 | ||||
-rw-r--r-- | application/security/SessionManager.php | 6 |
10 files changed, 347 insertions, 1 deletions
diff --git a/application/Utils.php b/application/Utils.php index 56f5b9a2..4b7fc546 100644 --- a/application/Utils.php +++ b/application/Utils.php | |||
@@ -159,7 +159,7 @@ function checkDateFormat($format, $string) | |||
159 | */ | 159 | */ |
160 | function generateLocation($referer, $host, $loopTerms = array()) | 160 | function generateLocation($referer, $host, $loopTerms = array()) |
161 | { | 161 | { |
162 | $finalReferer = '?'; | 162 | $finalReferer = './?'; |
163 | 163 | ||
164 | // No referer if it contains any value in $loopCriteria. | 164 | // No referer if it contains any value in $loopCriteria. |
165 | foreach (array_filter($loopTerms) as $value) { | 165 | foreach (array_filter($loopTerms) as $value) { |
diff --git a/application/container/ContainerBuilder.php b/application/container/ContainerBuilder.php new file mode 100644 index 00000000..e2c78ccc --- /dev/null +++ b/application/container/ContainerBuilder.php | |||
@@ -0,0 +1,81 @@ | |||
1 | <?php | ||
2 | |||
3 | declare(strict_types=1); | ||
4 | |||
5 | namespace Shaarli\Container; | ||
6 | |||
7 | use Shaarli\Bookmark\BookmarkFileService; | ||
8 | use Shaarli\Bookmark\BookmarkServiceInterface; | ||
9 | use Shaarli\Config\ConfigManager; | ||
10 | use Shaarli\History; | ||
11 | use Shaarli\Plugin\PluginManager; | ||
12 | use Shaarli\Render\PageBuilder; | ||
13 | use Shaarli\Security\LoginManager; | ||
14 | use Shaarli\Security\SessionManager; | ||
15 | |||
16 | /** | ||
17 | * Class ContainerBuilder | ||
18 | * | ||
19 | * Helper used to build a Slim container instance with Shaarli's object dependencies. | ||
20 | * Note that most injected objects MUST be added as closures, to let the container instantiate | ||
21 | * only the objects it requires during the execution. | ||
22 | * | ||
23 | * @package Container | ||
24 | */ | ||
25 | class ContainerBuilder | ||
26 | { | ||
27 | /** @var ConfigManager */ | ||
28 | protected $conf; | ||
29 | |||
30 | /** @var SessionManager */ | ||
31 | protected $session; | ||
32 | |||
33 | /** @var LoginManager */ | ||
34 | protected $login; | ||
35 | |||
36 | public function __construct(ConfigManager $conf, SessionManager $session, LoginManager $login) | ||
37 | { | ||
38 | $this->conf = $conf; | ||
39 | $this->session = $session; | ||
40 | $this->login = $login; | ||
41 | } | ||
42 | |||
43 | public function build(): ShaarliContainer | ||
44 | { | ||
45 | $container = new ShaarliContainer(); | ||
46 | $container['conf'] = $this->conf; | ||
47 | $container['sessionManager'] = $this->session; | ||
48 | $container['loginManager'] = $this->login; | ||
49 | $container['plugins'] = function (ShaarliContainer $container): PluginManager { | ||
50 | return new PluginManager($container->conf); | ||
51 | }; | ||
52 | |||
53 | $container['history'] = function (ShaarliContainer $container): History { | ||
54 | return new History($container->conf->get('resource.history')); | ||
55 | }; | ||
56 | |||
57 | $container['bookmarkService'] = function (ShaarliContainer $container): BookmarkServiceInterface { | ||
58 | return new BookmarkFileService( | ||
59 | $container->conf, | ||
60 | $container->history, | ||
61 | $container->loginManager->isLoggedIn() | ||
62 | ); | ||
63 | }; | ||
64 | |||
65 | $container['pageBuilder'] = function (ShaarliContainer $container): PageBuilder { | ||
66 | return new PageBuilder( | ||
67 | $container->conf, | ||
68 | $container->sessionManager->getSession(), | ||
69 | $container->bookmarkService, | ||
70 | $container->sessionManager->generateToken(), | ||
71 | $container->loginManager->isLoggedIn() | ||
72 | ); | ||
73 | }; | ||
74 | |||
75 | $container['pluginManager'] = function (ShaarliContainer $container): PluginManager { | ||
76 | return new PluginManager($container->conf); | ||
77 | }; | ||
78 | |||
79 | return $container; | ||
80 | } | ||
81 | } | ||
diff --git a/application/container/ShaarliContainer.php b/application/container/ShaarliContainer.php new file mode 100644 index 00000000..3fa9116e --- /dev/null +++ b/application/container/ShaarliContainer.php | |||
@@ -0,0 +1,30 @@ | |||
1 | <?php | ||
2 | |||
3 | declare(strict_types=1); | ||
4 | |||
5 | namespace Shaarli\Container; | ||
6 | |||
7 | use Shaarli\Bookmark\BookmarkServiceInterface; | ||
8 | use Shaarli\Config\ConfigManager; | ||
9 | use Shaarli\History; | ||
10 | use Shaarli\Plugin\PluginManager; | ||
11 | use Shaarli\Render\PageBuilder; | ||
12 | use Shaarli\Security\LoginManager; | ||
13 | use Shaarli\Security\SessionManager; | ||
14 | use Slim\Container; | ||
15 | |||
16 | /** | ||
17 | * Extension of Slim container to document the injected objects. | ||
18 | * | ||
19 | * @property ConfigManager $conf | ||
20 | * @property SessionManager $sessionManager | ||
21 | * @property LoginManager $loginManager | ||
22 | * @property History $history | ||
23 | * @property BookmarkServiceInterface $bookmarkService | ||
24 | * @property PageBuilder $pageBuilder | ||
25 | * @property PluginManager $pluginManager | ||
26 | */ | ||
27 | class ShaarliContainer extends Container | ||
28 | { | ||
29 | |||
30 | } | ||
diff --git a/application/front/ShaarliMiddleware.php b/application/front/ShaarliMiddleware.php new file mode 100644 index 00000000..fa6c6467 --- /dev/null +++ b/application/front/ShaarliMiddleware.php | |||
@@ -0,0 +1,57 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Shaarli\Front; | ||
4 | |||
5 | use Shaarli\Container\ShaarliContainer; | ||
6 | use Shaarli\Front\Exception\ShaarliException; | ||
7 | use Slim\Http\Request; | ||
8 | use Slim\Http\Response; | ||
9 | |||
10 | /** | ||
11 | * Class ShaarliMiddleware | ||
12 | * | ||
13 | * This will be called before accessing any Shaarli controller. | ||
14 | */ | ||
15 | class ShaarliMiddleware | ||
16 | { | ||
17 | /** @var ShaarliContainer contains all Shaarli DI */ | ||
18 | protected $container; | ||
19 | |||
20 | public function __construct(ShaarliContainer $container) | ||
21 | { | ||
22 | $this->container = $container; | ||
23 | } | ||
24 | |||
25 | /** | ||
26 | * Middleware execution: | ||
27 | * - execute the controller | ||
28 | * - return the response | ||
29 | * | ||
30 | * In case of error, the error template will be displayed with the exception message. | ||
31 | * | ||
32 | * @param Request $request Slim request | ||
33 | * @param Response $response Slim response | ||
34 | * @param callable $next Next action | ||
35 | * | ||
36 | * @return Response response. | ||
37 | */ | ||
38 | public function __invoke(Request $request, Response $response, callable $next) | ||
39 | { | ||
40 | try { | ||
41 | $response = $next($request, $response); | ||
42 | } catch (ShaarliException $e) { | ||
43 | $this->container->pageBuilder->assign('message', $e->getMessage()); | ||
44 | if ($this->container->conf->get('dev.debug', false)) { | ||
45 | $this->container->pageBuilder->assign( | ||
46 | 'stacktrace', | ||
47 | nl2br(get_class($this) .': '. $e->getTraceAsString()) | ||
48 | ); | ||
49 | } | ||
50 | |||
51 | $response = $response->withStatus($e->getCode()); | ||
52 | $response = $response->write($this->container->pageBuilder->render('error')); | ||
53 | } | ||
54 | |||
55 | return $response; | ||
56 | } | ||
57 | } | ||
diff --git a/application/front/controllers/LoginController.php b/application/front/controllers/LoginController.php new file mode 100644 index 00000000..ae3599e0 --- /dev/null +++ b/application/front/controllers/LoginController.php | |||
@@ -0,0 +1,48 @@ | |||
1 | <?php | ||
2 | |||
3 | declare(strict_types=1); | ||
4 | |||
5 | namespace Shaarli\Front\Controller; | ||
6 | |||
7 | use Shaarli\Front\Exception\LoginBannedException; | ||
8 | use Slim\Http\Request; | ||
9 | use Slim\Http\Response; | ||
10 | |||
11 | /** | ||
12 | * Class LoginController | ||
13 | * | ||
14 | * Slim controller used to render the login page. | ||
15 | * | ||
16 | * The login page is not available if the user is banned | ||
17 | * or if open shaarli setting is enabled. | ||
18 | * | ||
19 | * @package Front\Controller | ||
20 | */ | ||
21 | class LoginController extends ShaarliController | ||
22 | { | ||
23 | public function index(Request $request, Response $response): Response | ||
24 | { | ||
25 | if ($this->container->loginManager->isLoggedIn() | ||
26 | || $this->container->conf->get('security.open_shaarli', false) | ||
27 | ) { | ||
28 | return $response->withRedirect('./'); | ||
29 | } | ||
30 | |||
31 | $userCanLogin = $this->container->loginManager->canLogin($request->getServerParams()); | ||
32 | if ($userCanLogin !== true) { | ||
33 | throw new LoginBannedException(); | ||
34 | } | ||
35 | |||
36 | if ($request->getParam('username') !== null) { | ||
37 | $this->assignView('username', escape($request->getParam('username'))); | ||
38 | } | ||
39 | |||
40 | $this | ||
41 | ->assignView('returnurl', escape($request->getServerParam('HTTP_REFERER'))) | ||
42 | ->assignView('remember_user_default', $this->container->conf->get('privacy.remember_user_default', true)) | ||
43 | ->assignView('pagetitle', t('Login') .' - '. $this->container->conf->get('general.title', 'Shaarli')) | ||
44 | ; | ||
45 | |||
46 | return $response->write($this->render('loginform')); | ||
47 | } | ||
48 | } | ||
diff --git a/application/front/controllers/ShaarliController.php b/application/front/controllers/ShaarliController.php new file mode 100644 index 00000000..2b828588 --- /dev/null +++ b/application/front/controllers/ShaarliController.php | |||
@@ -0,0 +1,69 @@ | |||
1 | <?php | ||
2 | |||
3 | declare(strict_types=1); | ||
4 | |||
5 | namespace Shaarli\Front\Controller; | ||
6 | |||
7 | use Shaarli\Bookmark\BookmarkFilter; | ||
8 | use Shaarli\Container\ShaarliContainer; | ||
9 | |||
10 | abstract class ShaarliController | ||
11 | { | ||
12 | /** @var ShaarliContainer */ | ||
13 | protected $container; | ||
14 | |||
15 | /** @param ShaarliContainer $container Slim container (extended for attribute completion). */ | ||
16 | public function __construct(ShaarliContainer $container) | ||
17 | { | ||
18 | $this->container = $container; | ||
19 | } | ||
20 | |||
21 | /** | ||
22 | * Assign variables to RainTPL template through the PageBuilder. | ||
23 | * | ||
24 | * @param mixed $value Value to assign to the template | ||
25 | */ | ||
26 | protected function assignView(string $name, $value): self | ||
27 | { | ||
28 | $this->container->pageBuilder->assign($name, $value); | ||
29 | |||
30 | return $this; | ||
31 | } | ||
32 | |||
33 | protected function render(string $template): string | ||
34 | { | ||
35 | $this->assignView('linkcount', $this->container->bookmarkService->count(BookmarkFilter::$ALL)); | ||
36 | $this->assignView('privateLinkcount', $this->container->bookmarkService->count(BookmarkFilter::$PRIVATE)); | ||
37 | $this->assignView('plugin_errors', $this->container->pluginManager->getErrors()); | ||
38 | |||
39 | $this->executeDefaultHooks($template); | ||
40 | |||
41 | return $this->container->pageBuilder->render($template); | ||
42 | } | ||
43 | |||
44 | /** | ||
45 | * Call plugin hooks for header, footer and includes, specifying which page will be rendered. | ||
46 | * Then assign generated data to RainTPL. | ||
47 | */ | ||
48 | protected function executeDefaultHooks(string $template): void | ||
49 | { | ||
50 | $common_hooks = [ | ||
51 | 'includes', | ||
52 | 'header', | ||
53 | 'footer', | ||
54 | ]; | ||
55 | |||
56 | foreach ($common_hooks as $name) { | ||
57 | $plugin_data = []; | ||
58 | $this->container->pluginManager->executeHooks( | ||
59 | 'render_' . $name, | ||
60 | $plugin_data, | ||
61 | [ | ||
62 | 'target' => $template, | ||
63 | 'loggedin' => $this->container->loginManager->isLoggedIn() | ||
64 | ] | ||
65 | ); | ||
66 | $this->assignView('plugins_' . $name, $plugin_data); | ||
67 | } | ||
68 | } | ||
69 | } | ||
diff --git a/application/front/exceptions/LoginBannedException.php b/application/front/exceptions/LoginBannedException.php new file mode 100644 index 00000000..b31a4a14 --- /dev/null +++ b/application/front/exceptions/LoginBannedException.php | |||
@@ -0,0 +1,15 @@ | |||
1 | <?php | ||
2 | |||
3 | declare(strict_types=1); | ||
4 | |||
5 | namespace Shaarli\Front\Exception; | ||
6 | |||
7 | class LoginBannedException extends ShaarliException | ||
8 | { | ||
9 | public function __construct() | ||
10 | { | ||
11 | $message = t('You have been banned after too many failed login attempts. Try again later.'); | ||
12 | |||
13 | parent::__construct($message, 401); | ||
14 | } | ||
15 | } | ||
diff --git a/application/front/exceptions/ShaarliException.php b/application/front/exceptions/ShaarliException.php new file mode 100644 index 00000000..800bfbec --- /dev/null +++ b/application/front/exceptions/ShaarliException.php | |||
@@ -0,0 +1,23 @@ | |||
1 | <?php | ||
2 | |||
3 | declare(strict_types=1); | ||
4 | |||
5 | namespace Shaarli\Front\Exception; | ||
6 | |||
7 | use Throwable; | ||
8 | |||
9 | /** | ||
10 | * Class ShaarliException | ||
11 | * | ||
12 | * Abstract exception class used to defined any custom exception thrown during front rendering. | ||
13 | * | ||
14 | * @package Front\Exception | ||
15 | */ | ||
16 | abstract class ShaarliException extends \Exception | ||
17 | { | ||
18 | /** Override parent constructor to force $message and $httpCode parameters to be set. */ | ||
19 | public function __construct(string $message, int $httpCode, Throwable $previous = null) | ||
20 | { | ||
21 | parent::__construct($message, $httpCode, $previous); | ||
22 | } | ||
23 | } | ||
diff --git a/application/render/PageBuilder.php b/application/render/PageBuilder.php index 65e85aaf..f4fefda8 100644 --- a/application/render/PageBuilder.php +++ b/application/render/PageBuilder.php | |||
@@ -200,6 +200,23 @@ class PageBuilder | |||
200 | } | 200 | } |
201 | 201 | ||
202 | /** | 202 | /** |
203 | * Render a specific page as string (using a template file). | ||
204 | * e.g. $pb->render('picwall'); | ||
205 | * | ||
206 | * @param string $page Template filename (without extension). | ||
207 | * | ||
208 | * @return string Processed template content | ||
209 | */ | ||
210 | public function render(string $page): string | ||
211 | { | ||
212 | if ($this->tpl === false) { | ||
213 | $this->initialize(); | ||
214 | } | ||
215 | |||
216 | return $this->tpl->draw($page, true); | ||
217 | } | ||
218 | |||
219 | /** | ||
203 | * Render a 404 page (uses the template : tpl/404.tpl) | 220 | * Render a 404 page (uses the template : tpl/404.tpl) |
204 | * usage: $PAGE->render404('The link was deleted') | 221 | * usage: $PAGE->render404('The link was deleted') |
205 | * | 222 | * |
diff --git a/application/security/SessionManager.php b/application/security/SessionManager.php index b8b8ab8d..994fcbe5 100644 --- a/application/security/SessionManager.php +++ b/application/security/SessionManager.php | |||
@@ -196,4 +196,10 @@ class SessionManager | |||
196 | } | 196 | } |
197 | return true; | 197 | return true; |
198 | } | 198 | } |
199 | |||
200 | /** @return array Local reference to the global $_SESSION array */ | ||
201 | public function getSession(): array | ||
202 | { | ||
203 | return $this->session; | ||
204 | } | ||
199 | } | 205 | } |