]> git.immae.eu Git - github/shaarli/Shaarli.git/blob - application/front/ShaarliMiddleware.php
Move all admin controller into a dedicated group
[github/shaarli/Shaarli.git] / application / front / ShaarliMiddleware.php
1 <?php
2
3 namespace Shaarli\Front;
4
5 use Shaarli\Container\ShaarliContainer;
6 use Shaarli\Front\Exception\ShaarliFrontException;
7 use Shaarli\Front\Exception\UnauthorizedException;
8 use Slim\Http\Request;
9 use Slim\Http\Response;
10
11 /**
12 * Class ShaarliMiddleware
13 *
14 * This will be called before accessing any Shaarli controller.
15 */
16 class ShaarliMiddleware
17 {
18 /** @var ShaarliContainer contains all Shaarli DI */
19 protected $container;
20
21 public function __construct(ShaarliContainer $container)
22 {
23 $this->container = $container;
24 }
25
26 /**
27 * Middleware execution:
28 * - run updates
29 * - if not logged in open shaarli, redirect to login
30 * - execute the controller
31 * - return the response
32 *
33 * In case of error, the error template will be displayed with the exception message.
34 *
35 * @param Request $request Slim request
36 * @param Response $response Slim response
37 * @param callable $next Next action
38 *
39 * @return Response response.
40 */
41 public function __invoke(Request $request, Response $response, callable $next): Response
42 {
43 $this->initBasePath($request);
44
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
52 $this->runUpdates();
53 $this->checkOpenShaarli($request, $response, $next);
54
55 return $next($request, $response);
56 } catch (ShaarliFrontException $e) {
57 // Possible functional error
58 $this->container->pageBuilder->reset();
59 $this->container->pageBuilder->assign('message', nl2br($e->getMessage()));
60
61 $response = $response->withStatus($e->getCode());
62
63 return $response->write($this->container->pageBuilder->render('error', $this->container->basePath));
64 } catch (UnauthorizedException $e) {
65 $returnUrl = urlencode($this->container->environment['REQUEST_URI']);
66
67 return $response->withRedirect($this->container->basePath . '/login?returnurl=' . $returnUrl);
68 } catch (\Throwable $e) {
69 // Unknown error encountered
70 $this->container->pageBuilder->reset();
71 if ($this->container->conf->get('dev.debug', false)) {
72 $this->container->pageBuilder->assign('message', $e->getMessage());
73 $this->container->pageBuilder->assign(
74 'stacktrace',
75 nl2br(get_class($e) .': '. PHP_EOL . $e->getTraceAsString())
76 );
77 } else {
78 $this->container->pageBuilder->assign('message', t('An unexpected error occurred.'));
79 }
80
81 $response = $response->withStatus(500);
82
83 return $response->write($this->container->pageBuilder->render('error', $this->container->basePath));
84 }
85 }
86
87 /**
88 * Run the updater for every requests processed while logged in.
89 */
90 protected function runUpdates(): void
91 {
92 if ($this->container->loginManager->isLoggedIn() !== true) {
93 return;
94 }
95
96 $this->container->updater->setBasePath($this->container->basePath);
97 $newUpdates = $this->container->updater->update();
98 if (!empty($newUpdates)) {
99 $this->container->updater->writeUpdates(
100 $this->container->conf->get('resource.updates'),
101 $this->container->updater->getDoneUpdates()
102 );
103
104 $this->container->pageCacheManager->invalidateCaches();
105 }
106 }
107
108 /**
109 * Access is denied to most pages with `hide_public_links` + `force_login` settings.
110 */
111 protected function checkOpenShaarli(Request $request, Response $response, callable $next): bool
112 {
113 if (// if the user isn't logged in
114 !$this->container->loginManager->isLoggedIn()
115 // and Shaarli doesn't have public content...
116 && $this->container->conf->get('privacy.hide_public_links')
117 // and is configured to enforce the login
118 && $this->container->conf->get('privacy.force_login')
119 // and the current page isn't already the login page
120 // and the user is not requesting a feed (which would lead to a different content-type as expected)
121 && !in_array($next->getName(), ['login', 'atom', 'rss'], true)
122 ) {
123 throw new UnauthorizedException();
124 }
125
126 return true;
127 }
128
129 /**
130 * Initialize the URL base path if it hasn't been defined yet.
131 */
132 protected function initBasePath(Request $request): void
133 {
134 if (null === $this->container->basePath) {
135 $this->container->basePath = rtrim($request->getUri()->getBasePath(), '/');
136 }
137 }
138 }