Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 294
0.00% covered (danger)
0.00%
0 / 14
CRAP
0.00% covered (danger)
0.00%
0 / 1
tao_actions_Main
0.00% covered (danger)
0.00%
0 / 294
0.00% covered (danger)
0.00%
0 / 14
6320
0.00% covered (danger)
0.00%
0 / 1
 entry
0.00% covered (danger)
0.00%
0 / 36
0.00% covered (danger)
0.00%
0 / 1
132
 getDynamicConfigProvider
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 login
0.00% covered (danger)
0.00%
0 / 88
0.00% covered (danger)
0.00%
0 / 1
650
 logout
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
2
 index
0.00% covered (danger)
0.00%
0 / 70
0.00% covered (danger)
0.00%
0 / 1
56
 getNavigationElementsByGroup
0.00% covered (danger)
0.00%
0 / 27
0.00% covered (danger)
0.00%
0 / 1
72
 getMenuElementChildren
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
42
 getSections
0.00% covered (danger)
0.00%
0 / 32
0.00% covered (danger)
0.00%
0 / 1
110
 isReady
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 getUserService
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getSectionVisibilityFilter
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 getDefaultPageUrl
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 getSectionVisibilityByRoleFilter
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 isRestricted
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2
3/**
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; under version 2
7 * of the License (non-upgradable).
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17 *
18 * Copyright (c) 2002-2008 (original work) Public Research Centre Henri Tudor & University of Luxembourg
19 *                         (under the project TAO & TAO2);
20 *               2008-2010 (update and modification) Deutsche Institut für Internationale Pädagogische Forschung
21 *                         (under the project TAO-TRANSFER);
22 *               2009-2012 (update and modification) Public Research Centre Henri Tudor
23 *                         (under the project TAO-SUSTAIN & TAO-DEV);
24 *               2012-2018 (update and modification) Open Assessment Technologies SA;
25 *
26 */
27
28use oat\generis\model\user\UserRdf;
29use oat\oatbox\event\EventManager;
30use oat\oatbox\log\LoggerAwareTrait;
31use oat\oatbox\user\LoginService;
32use oat\tao\helpers\TaoCe;
33use oat\tao\model\accessControl\ActionResolver;
34use oat\tao\model\accessControl\func\AclProxy as FuncProxy;
35use oat\tao\model\action\ActionBlackList;
36use oat\tao\model\DynamicConfig\DynamicConfigProviderInterface;
37use oat\tao\model\entryPoint\EntryPointService;
38use oat\tao\model\event\LoginFailedEvent;
39use oat\tao\model\event\LoginSucceedEvent;
40use oat\tao\model\event\LogoutSucceedEvent;
41use oat\tao\model\menu\MenuService;
42use oat\tao\model\menu\Perspective;
43use oat\tao\model\menu\SectionVisibilityByRoleFilter;
44use oat\tao\model\menu\SectionVisibilityFilter;
45use oat\tao\model\menu\SectionVisibilityFilterInterface;
46use oat\tao\model\mvc\DefaultUrlService;
47use oat\tao\model\notification\Notification;
48use oat\tao\model\notification\NotificationServiceInterface;
49use oat\tao\model\user\UserLocks;
50use tao_helpers_Display as DisplayHelper;
51
52/**
53 * @author CRP Henri Tudor - TAO Team - {@link http://www.tao.lu}
54 * @license GPLv2  http://www.opensource.org/licenses/gpl-2.0.php
55 * @package tao
56 *
57 */
58class tao_actions_Main extends tao_actions_CommonModule
59{
60    use LoggerAwareTrait;
61
62    /** @var SectionVisibilityFilterInterface */
63    private $sectionVisibilityFilter;
64
65    /**
66     * First page, when arriving on a system
67     * to choose front or back office
68     */
69    public function entry()
70    {
71        $this->defaultData();
72        $entries = [];
73        foreach (EntryPointService::getRegistry()->getEntryPoints() as $entry) {
74            if (tao_models_classes_accessControl_AclProxy::hasAccessUrl($entry->getUrl())) {
75                $entries[] = $entry;
76            }
77        }
78
79        if (empty($entries)) {
80            // no access -> error
81            if (common_session_SessionManager::isAnonymous()) {
82                /* @var $urlRouteService DefaultUrlService */
83                $urlRouteService = $this->getServiceLocator()->get(DefaultUrlService::SERVICE_ID);
84                $this->redirect($urlRouteService->getLoginUrl());
85            } else {
86                common_session_SessionManager::endSession();
87                return $this->returnError(__('You currently have no access to the platform'), true, 403);
88            }
89        } elseif (count($entries) == 1 && !common_session_SessionManager::isAnonymous()) {
90            // single entrypoint -> redirect
91            $entry = current($entries);
92            return $this->redirect($entry->getUrl());
93        } else {
94            // multiple entries -> choice
95            if (!common_session_SessionManager::isAnonymous()) {
96                $this->setData('user', $this->getSession()->getUserLabel());
97            }
98            $this->setData('entries', $entries);
99            $naviElements = $this->getNavigationElementsByGroup('settings');
100            foreach ($naviElements as $key => $naviElement) {
101                if ($naviElement['perspective']->getId() !== 'user_settings') {
102                    unset($naviElements[$key]);
103                    continue;
104                }
105            }
106
107            if ($this->hasRequestParameter('errorMessage')) {
108                $this->setData('errorMessage', $this->getRequestParameter('errorMessage'));
109            }
110            $this->setData('logout', $this->getServiceLocator()->get(DefaultUrlService::SERVICE_ID)->getLogoutUrl());
111            $this->setData('userLabel', $this->getSession()->getUserLabel());
112            $this->setData(
113                'portalUrl',
114                $this->getDynamicConfigProvider()->getConfigByName(
115                    DynamicConfigProviderInterface::PLATFORM_URL_CONFIG_NAME
116                )
117            );
118            $this->setData('settings-menu', $naviElements);
119            $this->setData('current-section', $this->getRequestParameter('section'));
120            $this->setData('content-template', ['blocks/entry-points.tpl', 'tao']);
121            $this->setView('layout.tpl', 'tao');
122        }
123    }
124
125    public function getDynamicConfigProvider(): DynamicConfigProviderInterface
126    {
127        return $this->getServiceLocator()->getContainer()->get(DynamicConfigProviderInterface::class);
128    }
129
130    /**
131     * Authentication form,
132     * default page, main entry point to the user
133     * @return void
134     * @throws Exception
135     * @throws common_ext_ExtensionException
136     * @throws core_kernel_persistence_Exception
137     */
138    public function login()
139    {
140        $this->defaultData();
141        /** @var common_ext_ExtensionsManager $extensionManager */
142        $extensionManager = $this->getServiceLocator()->get(common_ext_ExtensionsManager::SERVICE_ID);
143        $extension = $extensionManager->getExtensionById('tao');
144        $config = $extension->getConfig('login');
145
146        $disableAutoComplete = !empty($config['disableAutocomplete']);
147        $enablePasswordReveal = !empty($config['enablePasswordReveal']);
148        $disableAutofocus = !empty($config['disableAutofocus']);
149
150        $enableIframeProtection = !empty($config['block_iframe_usage']) && $config['block_iframe_usage'];
151        if ($enableIframeProtection) {
152            \oat\tao\model\security\IFrameBlocker::setHeader();
153        }
154
155        $params = [
156            'disableAutocomplete' => $disableAutoComplete,
157            'enablePasswordReveal' => $enablePasswordReveal,
158        ];
159
160        if ($this->hasRequestParameter('redirect')) {
161            $redirectUrl = $_REQUEST['redirect'];
162
163            if (substr($redirectUrl, 0, 1) == '/' || substr($redirectUrl, 0, strlen(ROOT_URL)) == ROOT_URL) {
164                $params['redirect'] = $redirectUrl;
165            }
166        }
167
168        $container = new tao_actions_form_Login($params);
169        $form = $container->getForm();
170
171        if ($form->isSubmited()) {
172            if ($form->isValid()) {
173
174                /** @var UserLocks $userLocksService */
175                $userLocksService = $this->getServiceLocator()->get(UserLocks::SERVICE_ID);
176                /** @var EventManager $eventManager */
177                $eventManager = $this->getServiceLocator()->get(EventManager::SERVICE_ID);
178
179                try {
180                    if ($userLocksService->isLocked($form->getValue('login'))) {
181                        $this->logInfo("User '" . $form->getValue('login') . "' has been locked.");
182
183                        $statusDetails = $userLocksService->getStatusDetails($form->getValue('login'));
184                        if ($statusDetails['auto']) {
185                            $msg = __('You have been locked due to too many failed login attempts. ');
186                            if ($userLocksService->getOption(UserLocks::OPTION_USE_HARD_LOCKOUT)) {
187                                $msg .= __('Please contact your administrator.');
188                            } else {
189                                /** @var DateInterval $remaining */
190                                $remaining = $statusDetails['remaining'];
191
192                                $reference = new DateTimeImmutable();
193                                $endTime = $reference->add($remaining);
194
195                                $diffInSeconds = $endTime->getTimestamp() - $reference->getTimestamp();
196
197                                $humanDiff = $diffInSeconds > 60
198                                    ? tao_helpers_Date::displayInterval(
199                                        $statusDetails['remaining'],
200                                        tao_helpers_Date::FORMAT_INTERVAL_LONG
201                                    )
202                                    : $diffInSeconds . ' ' . ($diffInSeconds === 1 ? __('second') : __('seconds'));
203
204                                $msg .= __('Please try in %s.', $humanDiff);
205                            }
206                        } else {
207                            $msg = __('Your account has been locked, please contact your administrator.');
208                        }
209
210                        $this->setData('errorMessage', $msg);
211                    } else {
212                        if (LoginService::login($form->getValue('login'), $form->getValue('password'))) {
213                            $logins = $this->getSession()->getUser()->getPropertyValues(UserRdf::PROPERTY_LOGIN);
214
215                            $eventManager->trigger(new LoginSucceedEvent(current($logins)));
216
217                            $this->logInfo("Successful login of user '" . $form->getValue('login') . "'.");
218
219                            if (
220                                $this->hasRequestParameter('redirect')
221                                && tao_models_classes_accessControl_AclProxy::hasAccessUrl($_REQUEST['redirect'])
222                            ) {
223                                $this->redirect($_REQUEST['redirect']);
224                            } else {
225                                $this->forward('entry');
226                            }
227                        } else {
228                            $eventManager->trigger(new LoginFailedEvent($form->getValue('login')));
229
230                            $this->logInfo("Unsuccessful login of user '" . $form->getValue('login') . "'.");
231
232                            $msg = __('Invalid login or password. Please try again.');
233
234                            if ($userLocksService->getOption(UserLocks::OPTION_USE_HARD_LOCKOUT)) {
235                                $remainingAttempts = $userLocksService->getLockoutRemainingAttempts(
236                                    $form->getValue('login')
237                                );
238                                if ($remainingAttempts !== false) {
239                                    if ($remainingAttempts === 0) {
240                                        // phpcs:disable Generic.Files.LineLength
241                                        $msg = __('Invalid login or password. Your account has been locked, please contact your administrator.');
242                                    // phpcs:enable Generic.Files.LineLength
243                                    } else {
244                                        $msg .= ' ' . (
245                                            $remainingAttempts === 1
246                                                // phpcs:disable Generic.Files.LineLength
247                                                ? __('Last attempt before your account is locked.')
248                                                : __('%d attempts left before your account is locked.', $remainingAttempts)
249                                            // phpcs:enable Generic.Files.LineLength
250                                        );
251                                    }
252                                }
253                            }
254
255                            $this->setData('errorMessage', $msg);
256                        }
257                    }
258                } catch (core_kernel_users_Exception $e) {
259                    $this->setData('errorMessage', __('Invalid login or password. Please try again.'));
260                }
261            } else {
262                foreach ($form->getElements() as $formElement) {
263                    $fieldError = $formElement->getError();
264                    if ($fieldError) {
265                        $this->setData('fieldMessages_' . $formElement->getName(), $fieldError);
266                    }
267                }
268            }
269        }
270
271        $this->setData('title', __("TAO Login"));
272
273        $this->setData('autocompleteDisabled', (int)$disableAutoComplete);
274        $this->setData('passwordRevealEnabled', (int)$enablePasswordReveal);
275        $this->setData('autofocusDisabled', (int)$disableAutofocus);
276
277        $entryPointService = $this->getServiceLocator()->get(EntryPointService::SERVICE_ID);
278        $this->setData('entryPoints', $entryPointService->getEntryPoints(EntryPointService::OPTION_PRELOGIN));
279
280        if ($this->hasRequestParameter('msg')) {
281            $this->setData('msg', $this->getRequestParameter('msg'));
282        }
283
284        $this->setData('show_gdpr', !empty($config['show_gdpr']) && $config['show_gdpr']);
285
286        $this->setData('content-template', 'login');
287        $this->setData('hideLogo', $config['hideLogo'] ?? false);
288
289        $this->setView('layout.tpl', 'tao');
290    }
291
292    /**
293     * Logout, destroy the session and back to the login page
294     */
295    public function logout()
296    {
297        $this->defaultData();
298        $eventManager = $this->getServiceLocator()->get(EventManager::SERVICE_ID);
299
300        $logins = $this->getSession()->getUser()->getPropertyValues(UserRdf::PROPERTY_LOGIN);
301        $eventManager->trigger(new LogoutSucceedEvent(current($logins)));
302
303        /* @var $urlRouteService DefaultUrlService */
304        $urlRouteService = $this->getServiceLocator()->get(DefaultUrlService::SERVICE_ID);
305
306        try {
307            $this->redirect((string)$urlRouteService->getRedirectUrl('logout'));
308        } finally {
309            common_session_SessionManager::endSession();
310        }
311    }
312
313    /**
314     * The main action, load the layout
315     *
316     * @return void
317     */
318    public function index()
319    {
320        $this->defaultData();
321        $user      = $this->getUserService()->getCurrentUser();
322        $extension = $this->getRequestParameter('ext');
323        $structure = $this->getRequestParameter('structure');
324
325        if ($this->hasRequestParameter('structure')) {
326            // structured mode
327            // @todo stop using session to manage uri/classUri
328            $this->removeSessionAttribute('uri');
329            $this->removeSessionAttribute('classUri');
330            $this->removeSessionAttribute('showNodeUri');
331
332            TaoCe::setLastVisitedUrl(
333                _url(
334                    'index',
335                    'Main',
336                    'tao',
337                    [
338                        'structure' => $structure,
339                        'ext'       => $extension
340                    ]
341                )
342            );
343
344            $sections = $this->getSections($extension, $structure);
345
346            if (count($sections) > 0) {
347                $this->setData('sections', $sections);
348            } else {
349                $this->logWarning('no sections');
350                $this->redirect($this->getDefaultPageUrl());
351            }
352        } else {
353            //check if the user is a noob, otherwise redirect him to his last visited extension.
354            $firstTime = TaoCe::isFirstTimeInTao();
355            if ($firstTime == false) {
356                $lastVisited = TaoCe::getLastVisitedUrl();
357
358                if (!is_null($lastVisited)) {
359                    $this->redirect($lastVisited);
360                }
361            }
362        }
363
364        $this->setData(
365            'taoAsATool',
366            $this->getDynamicConfigProvider()->hasConfig(DynamicConfigProviderInterface::PLATFORM_URL_CONFIG_NAME)
367        );
368
369        $perspectiveTypes = [Perspective::GROUP_DEFAULT, 'settings', 'persistent'];
370        foreach ($perspectiveTypes as $perspectiveType) {
371            $this->setData($perspectiveType . '-menu', $this->getNavigationElementsByGroup($perspectiveType));
372        }
373
374        /* @var $notifService NotificationServiceInterface */
375        $notifService = $this->getServiceLocator()->get(NotificationServiceInterface::SERVICE_ID);
376
377        if ($notifService->getVisibility()) {
378            $notif = $notifService->notificationCount($user->getUri());
379
380            $this->setData('unread-notification', $notif[Notification::CREATED_STATUS]);
381
382            $this->setData('notification-url', _url(
383                'index',
384                'Main',
385                'tao',
386                [
387                    'structure' => 'tao_Notifications',
388                    'ext'       => 'tao',
389                    'section'   => 'settings_my_notifications',
390                ]
391            ));
392        }
393        /* @var $urlRouteService DefaultUrlService */
394        $urlRouteService = $this->getServiceLocator()->get(DefaultUrlService::SERVICE_ID);
395        $this->setData('logout', $urlRouteService->getLogoutUrl());
396
397        $this->setData('user_lang', $this->getSession()->getDataLanguage());
398        $this->setData('userLabel', DisplayHelper::htmlEscape($this->getSession()->getUserLabel()));
399        $this->setData(
400            'portalUrl',
401            $this->getDynamicConfigProvider()->getConfigByName(
402                DynamicConfigProviderInterface::PLATFORM_URL_CONFIG_NAME
403            )
404        );
405        // re-added to highlight selected extension in menu
406        $this->setData('shownExtension', $extension);
407        $this->setData('shownStructure', $structure);
408
409        $this->setData('current-section', $this->getRequestParameter('section'));
410
411        //creates the URL of the action used to configure the client side
412        $clientConfigParams = [
413            'shownExtension' => $extension,
414            'shownStructure' => $structure
415        ];
416        $this->setData('client_config_url', $this->getClientConfigUrl($clientConfigParams));
417        $this->setData('content-template', ['blocks/sections.tpl', 'tao']);
418
419        $this->setView('layout.tpl', 'tao');
420    }
421
422    /**
423     * Get perspective data depending on the group set in structure.xml
424     *
425     * @param $groupId
426     * @return array
427     */
428    private function getNavigationElementsByGroup($groupId)
429    {
430        $entries = [];
431
432        /**
433         * @var int $i
434         * @var  Perspective $perspective
435         */
436        foreach (MenuService::getPerspectivesByGroup($groupId) as $i => $perspective) {
437            if ($this->isRestricted($perspective)) {
438                $this->logDebug(
439                    sprintf(
440                        "Perspective %s has been ignored base on Section Visibility Filter",
441                        $perspective->getId()
442                    )
443                );
444                continue;
445            }
446
447            $binding = $perspective->getBinding();
448            $children = $this->getMenuElementChildren($perspective);
449
450            if (!empty($binding) || !empty($children)) {
451                $entry = [
452                    'perspective' => $perspective,
453                    'children'    => $children
454                ];
455                if (!is_null($binding)) {
456                    $entry['binding'] = $perspective->getExtension() . '/' . $binding;
457                }
458                $entries[$i] = $entry;
459            }
460
461            //We want to always keep user settings menu dropdown even if there is no children to place logout button.
462            if (empty($children) && $perspective->getId() === 'user_settings') {
463                $entry = [
464                    'perspective' => $perspective,
465                    'children'    => []
466                ];
467
468                $entries[$i] = $entry;
469            }
470        }
471        return $entries;
472    }
473
474    /**
475     * Get nested menu elements depending on user rights.
476     *
477     * @param Perspective $menuElement from the structure.xml
478     * @return array menu elements list
479     */
480    private function getMenuElementChildren(Perspective $menuElement)
481    {
482        $user = $this->getSession()->getUser();
483        $children = [];
484        foreach ($menuElement->getChildren() as $section) {
485            try {
486                $resolver = new ActionResolver($section->getUrl());
487
488                if (!$this->getSectionVisibilityFilter()->isVisible($section->getId())) {
489                    continue;
490                }
491
492                if (
493                    !$this->getSectionVisibilityByRoleFilter()->isVisible(
494                        $this->getSession()->getUserRoles(),
495                        $section->getId()
496                    )
497                ) {
498                    continue;
499                }
500
501                if (FuncProxy::accessPossible($user, $resolver->getController(), $resolver->getAction())) {
502                    $children[] = $section;
503                }
504            } catch (ResolverException $e) {
505                $this->logWarning('Invalid reference in structures: ' . $e->getMessage());
506            }
507        }
508        return $children;
509    }
510
511    /**
512     * Get the sections of the current extension's structure
513     *
514     * @param string $shownExtension
515     * @param string $shownStructure
516     * @return array the sections
517     */
518    private function getSections($shownExtension, $shownStructure)
519    {
520
521        $sections = [];
522        $user = $this->getSession()->getUser();
523        $structure = MenuService::getPerspective($shownExtension, $shownStructure);
524        $sectionVisibilityFilter = $this->getSectionVisibilityFilter();
525
526        if (!is_null($structure)) {
527            foreach ($structure->getChildren() as $section) {
528                $resolver = new ActionResolver($section->getUrl());
529
530                if (!$sectionVisibilityFilter->isVisible($section->getId())) {
531                    continue;
532                }
533
534                if (
535                    !$this->getSectionVisibilityByRoleFilter()->isVisible(
536                        $this->getSession()->getUserRoles(),
537                        $section->getId()
538                    )
539                ) {
540                    continue;
541                }
542
543                if (FuncProxy::accessPossible($user, $resolver->getController(), $resolver->getAction())) {
544                    foreach ($section->getActions() as $action) {
545                        $sectionPath = $sectionVisibilityFilter->createSectionPath(
546                            [
547                                $section->getId(),
548                                $action->getId()
549                            ]
550                        );
551
552                        if (!$sectionVisibilityFilter->isVisible($sectionPath)) {
553                            $section->removeAction($action);
554
555                            continue;
556                        }
557
558                        $this->propagate($action);
559                        $resolver = new ActionResolver($action->getUrl());
560                        if (
561                            !FuncProxy::accessPossible($user, $resolver->getController(), $resolver->getAction()) ||
562                            $this->getServiceLocator()->get(ActionBlackList::SERVICE_ID)->isDisabled($action->getId())
563                        ) {
564                            $section->removeAction($action);
565                        }
566                    }
567
568                    $sections[] = $section;
569                }
570            }
571        }
572
573        return $sections;
574    }
575
576
577    /**
578     * Check if the system is ready
579     */
580    public function isReady()
581    {
582        if ($this->isXmlHttpRequest()) {
583            // the default ajax response is successful style rastafarai
584            $ajaxResponse = new common_AjaxResponse();
585        } else {
586            throw new common_exception_IsAjaxAction(__CLASS__ . '::' . __METHOD__ . '()');
587        }
588    }
589
590    /**
591     * @return tao_models_classes_UserService
592     */
593    protected function getUserService()
594    {
595        return $this->getServiceLocator()->get(tao_models_classes_UserService::SERVICE_ID);
596    }
597
598    private function getSectionVisibilityFilter(): SectionVisibilityFilter
599    {
600        if (empty($this->sectionVisibilityFilter)) {
601            $this->sectionVisibilityFilter = $this->getServiceLocator()->get(SectionVisibilityFilter::SERVICE_ID);
602        }
603
604        return $this->sectionVisibilityFilter;
605    }
606
607    /**
608     * @throws Exception
609     */
610    private function getDefaultPageUrl(): string
611    {
612        $firstItem = current(MenuService::getPerspectivesByGroup(Perspective::GROUP_DEFAULT));
613        if (!$firstItem instanceof Perspective) {
614            throw new Exception('Item should be instance of Perspective');
615        }
616
617        return $firstItem->getUrl();
618    }
619
620    private function getSectionVisibilityByRoleFilter(): SectionVisibilityByRoleFilter
621    {
622        return $this->getPsrContainer()->get(SectionVisibilityByRoleFilter::class);
623    }
624
625    private function isRestricted(Perspective $perspective)
626    {
627        return !$this->getSectionVisibilityFilter()->isVisible($perspective->getId())
628            || !$this->getSectionVisibilityByRoleFilter()->isVisible(
629                $this->getSession()->getUserRoles(),
630                $perspective->getId()
631            );
632    }
633}