Lines 88.66% 133 / 150
Methods 78.57% 11 / 14
Classes 0.00% 0 / 1
Name Lines Methods CRAP
 readResponse 95.00% 38 / 40 0.00% 0 / 1 8
 testLogin 75.86% 44 / 58 0.00% 0 / 1 6.51
 getProviderList 100.00% 7 / 7 100.00% 1 / 1 4
 [BO\Zmsstatistic\BaseController] __invoke 100.00% 5 / 5 100.00% 1 / 1 2
 [BO\Zmsstatistic\Helper\Access] initAccessRights 100.00% 6 / 6 100.00% 1 / 1 4
 [BO\Zmsstatistic\Helper\Access] readWorkstation 100.00% 2 / 2 100.00% 1 / 1 1
 [BO\Zmsstatistic\Helper\Access] readDepartment 100.00% 5 / 5 100.00% 1 / 1 2
 [BO\Zmsstatistic\Helper\Access] readOrganisation 100.00% 5 / 5 100.00% 1 / 1 2
 [BO\Zmsstatistic\Helper\Access] readOwner 100.00% 5 / 5 100.00% 1 / 1 2
 [BO\Zmsstatistic\Helper\Access] validateAccessRights 100.00% 3 / 3 100.00% 1 / 1 1
 [BO\Zmsstatistic\Helper\Access] validateAccess 100.00% 4 / 4 100.00% 1 / 1 7
 [BO\Zmsstatistic\Helper\Access] validateScope 66.66% 2 / 3 0.00% 0 / 1 4.59
 [BO\Zmsstatistic\Helper\Access] isPathWithoutScope 100.00% 5 / 5 100.00% 1 / 1 4
 [BO\Zmsstatistic\Helper\Access] exceptionTemplateExists 100.00% 2 / 2 100.00% 1 / 1 1
17class Index extends BaseController
18{
19    protected bool $withAccess = false;
20
21    /**
22     * @SuppressWarnings(Param)
23     * @return \Psr\Http\Message\ResponseInterface
24     */
25    #[\Override]
26    public function readResponse(
27        RequestInterface $request,
28        ResponseInterface $response,
29        array $args
30    ): ResponseInterface {
31        try {
32            $workstation = \App::http()->readGetResult('/workstation/')->getEntity();
33        } catch (\Exception $workstationexception) {
34            $workstation = null;
35        }
36
37        $config = \App::http()->readGetResult('/config/', [], \App::CONFIG_SECURE_TOKEN)->getEntity();
38        /** @var \Psr\Http\Message\ServerRequestInterface $request */
39        $input = $request->getParsedBody();
40        $oidclogin = $request->getAttribute('validator')->getParameter('oidclogin')->isString()->getValue();
41        if ($request->getMethod() === 'POST') {
42            $loginData = $this->testLogin($input);
43            if ($loginData instanceof Workstation && $loginData->offsetExists('authkey')) {
44                \BO\Zmsclient\Auth::setKey($loginData->authkey);
45                return ModuleAccess::rejectWrongModuleAccess(ModuleAccess::MODULE_STATISTIC, $loginData, $response)
46                    ?? \BO\Slim\Render::redirect('workstationSelect', array(), array());
47            }
48
49            return Render::withHtml(
50                $response,
51                'page/index.twig',
52                array(
53                    'title' => 'Anmeldung gescheitert',
54                    'loginfailed' => true,
55                    'workstation' => null,
56                    'exception' => $loginData,
57                    'oidcproviderlist' => $this->getProviderList($config),
58                    'oidclogin' => $oidclogin,
59                    'showloginform' => (! $oidclogin)
60                )
61            );
62        } else {
63            if ($workstation instanceof Workstation && $workstation->hasId()) {
64                if ($wrongModuleResponse = ModuleAccess::rejectWrongModuleAccess(ModuleAccess::MODULE_STATISTIC, $workstation, $response)) {
65                    return $wrongModuleResponse;
66                }
67            }
68            return Render::withHtml(
69                $response,
70                'page/index.twig',
71                array(
72                    'title' => 'Anmeldung',
73                    'config' => $config,
74                    'workstation' => $workstation,
75                    'oidcproviderlist' => $this->getProviderList($config),
76                    'oidclogin' => $oidclogin,
77                    'showloginform' => (! $oidclogin)
78                )
79            );
80        }
81    }
82
83    /**
84     * @return mixed
85     */
86    #[\Override]
87    protected function testLogin(mixed $input): mixed
88    {
89        $userAccount = new Useraccount(array(
90            'id' => $input['loginName'],
91            'password' => $input['password'],
92            'departments' => array('id' => 0) // required in schema validation
93        ));
94        try {
95            /** @var mixed $workstation */
96            $workstation = \App::http()->readPostResult('/workstation/login/', $userAccount)->getEntity();
97
98            /** @var mixed $authkey */
99            $authkey = $workstation->authkey;
100            $sessionHash = hash('sha256', $authkey);
101            \App::$log->info('Login successful', [
102                'event' => 'auth_login_success',
103                'timestamp' => date('c'),
104                'username' => $userAccount->id,
105                'hashed_session_token' => $sessionHash,
106                'application' => 'zmsstatistic'
107            ]);
108
109            return $workstation;
110        } catch (\BO\Zmsclient\Exception $exception) {
111            $template = Helper\TwigExceptionHandler::getExceptionTemplate($exception);
112            if ('BO\Zmsentities\Exception\SchemaValidation' == $exception->template) {
113                $exceptionData = [
114                  'template' => 'exception/bo/zmsbackend/useraccount/exception/invalidcredentials.twig'
115                ];
116                $exceptionData['data']['password']['messages'] = [
117                    'Der Nutzername oder das Passwort wurden falsch eingegeben'
118                ];
119                \App::$log->info('Login failed - invalid credentials', [
120                    'event' => 'auth_login_failed',
121                    'timestamp' => date('c'),
122                    'username' => $userAccount->id,
123                    'error_type' => 'invalid_credentials',
124                    'application' => 'zmsstatistic'
125                ]);
126            } elseif ('BO\Zmsbackend\Useraccount\Exception\UserAlreadyLoggedIn' == $exception->template) {
127                \BO\Zmsclient\Auth::setKey($exception->data['authkey'], time() + \App::SESSION_DURATION);
128                \App::$log->info('User already logged in - reusing existing session', [
129                    'event' => 'auth_session_reuse',
130                    'timestamp' => date('c'),
131                    'username' => $userAccount->id,
132                    'hashed_session_token' => hash('sha256', $exception->data['authkey']),
133                    'application' => 'zmsstatistic'
134                ]);
135                throw $exception;
136            } elseif (
137                '' != $exception->template
138                && $this->exceptionTemplateExists($template)
139            ) {
140                $exceptionData = [
141                  'template' => $template,
142                  'data' => $exception->data
143                ];
144                \App::$log->info('Login failed - other error', [
145                    'event' => 'auth_login_failed',
146                    'timestamp' => date('c'),
147                    'username' => $userAccount->id,
148                    'error_type' => 'other',
149                    'error_message' => $exception->getMessage(),
150                    'application' => 'zmsstatistic'
151                ]);
152            } else {
153                throw $exception;
154            }
155        }
156        return $exceptionData;
157    }
158    protected function getProviderList(mixed $config): array
159    {
160        $allowedProviderList = explode(',', $config->getPreference('oidc', 'provider') ?? '');
161        $oidcproviderlist = [];
162        foreach (\BO\Slim\Middleware\OAuthMiddleware::$authInstances as $provider => $authInstance) {
163            if (
164                class_exists($authInstance) &&
165                in_array($provider, $allowedProviderList)
166            ) {
167                $oidcproviderlist[] = $provider;
168            }
169        }
170        return $oidcproviderlist;
171    }
172}

Inherited from BO\Zmsstatistic\BaseController

20    public function __invoke(RequestInterface $request, ResponseInterface $response, array $args)
21    {
22        $request = $this->initRequest($request);
23        if ($this->withAccess) {
24            $this->initAccessRights($request);
25        }
26        $noCacheResponse = \BO\Slim\Render::withLastModified($response, time(), '0');
27        return $this->readResponse($request, $noCacheResponse, $args);
28    }

Inherited from BO\Zmsstatistic\Helper\Access

33    protected function initAccessRights(RequestInterface $request): void
34    {
35        $this->workstation = $this->readWorkstation();
36        if ($this->workstation && isset($this->workstation->scope['id']) && $this->workstation->scope['id'] > 0) {
37            $this->department = $this->readDepartment();
38            $this->organisation = $this->readOrganisation();
39            $this->owner = $this->readOwner();
40        }
41        $this->validateAccessRights($request);
42    }
44    protected function readWorkstation(): mixed
45    {
46        $workstation = \App::http()->readGetResult('/workstation/', ['resolveReferences' => $this->resolveLevel]);
47        return $workstation->getEntity();
48    }
50    protected function readDepartment(): mixed
51    {
52        if ($this->workstation->getUseraccount()->hasPermissions(['statistic'])) {
53            return \App::http()
54                ->readGetResult('/scope/' . $this->workstation->scope['id'] . '/department/')
55                ->getEntity();
56        }
57        return null;
58    }
60    protected function readOrganisation(): mixed
61    {
62        if ($this->workstation->getUseraccount()->isSuperUser()) {
63            return \App::http()
64                ->readGetResult('/department/' . $this->department->getId() . '/organisation/')
65                ->getEntity();
66        }
67        return null;
68    }
70    protected function readOwner(): mixed
71    {
72        if ($this->workstation->getUseraccount()->isSuperUser()) {
73            return \App::http()
74                ->readGetResult('/organisation/' . $this->organisation->getId() . '/owner/')
75                ->getEntity();
76        }
77        return null;
78    }
80    protected function validateAccessRights(RequestInterface $request): void
81    {
82        $path = $request->getUri()->getPath();
83        $this->validateAccess($path);
84        $this->validateScope($path);
85    }
87    protected function validateAccess(string $path): void
88    {
89        if (
90            (false !== strpos($path, 'owner') && ! $this->owner) ||
91            (false !== strpos($path, 'organisation') && ! $this->organisation) ||
92            (false !== strpos($path, 'department') && ! $this->department)
93        ) {
94            throw new UserAccountAccessRightsFailed();
95        }
96    }
98    protected function validateScope(string $path): void
99    {
100        if (
101            $this->isPathWithoutScope($path)
102            && (! isset($this->workstation['scope']) || ! isset($this->workstation['scope']['id']))
103        ) {
104            throw new WorkstationMissingScope();
105        }
106    }
108    protected function isPathWithoutScope(string $path): bool
109    {
110        // TODO: refactor to integrate these access rules in the controller to make them visible
111        return (false === strpos($path, 'select')
112            && false === strpos($path, 'warehouse')
113            && false === strpos($path, 'logout')
114            && false === strpos($path, 'report')
115        );
116    }
159    protected function exceptionTemplateExists(string $template): bool
160    {
161        /** @var mixed $container */
162        $container = \App::$slim->getContainer();
163        return $container->get('view')->getLoader()->exists($template);
164    }