Lines 96.00% 144 / 150
Methods 71.42% 5 / 7
Classes 0.00% 0 / 1
Name Lines Methods CRAP
 readResponse 97.77% 44 / 45 0.00% 0 / 1 11
 testLogin 100.00% 57 / 57 100.00% 1 / 1 6
 getProviderList 100.00% 7 / 7 100.00% 1 / 1 4
 [BO\Zmsadmin\BaseController] __invoke 100.00% 3 / 3 100.00% 1 / 1 1
 [BO\Zmsadmin\BaseController] getSchemaConstraintList 100.00% 8 / 8 100.00% 1 / 1 4
 [BO\Zmsadmin\BaseController] transformValidationErrors 61.53% 8 / 13 0.00% 0 / 1 15.69
 [BO\Zmsadmin\BaseController] handleEntityWrite 100.00% 17 / 17 100.00% 1 / 1 5
17class Index extends BaseController
18{
19    /**
20     * @SuppressWarnings(Param)
21     * @return \Psr\Http\Message\ResponseInterface
22     */
23    #[\Override]
24    public function readResponse(
25        \Psr\Http\Message\RequestInterface $request,
26        \Psr\Http\Message\ResponseInterface $response,
27        array $args
28    ): \Psr\Http\Message\ResponseInterface {
29        try {
30            $workstation = \App::$http->readGetResult('/workstation/')->getEntity();
31        } catch (\Exception $workstationexception) {
32            $workstation = null;
33        }
34        $config = \App::$http->readGetResult('/config/', [], \App::CONFIG_SECURE_TOKEN)->getEntity();
35        $input = $request->getParsedBody();
36        $oidclogin = $request->getAttribute('validator')->getParameter('oidclogin')->isString()->getValue();
37        if ($request->getMethod() === 'POST') {
38            $loginData = $this->testLogin($input);
39            if ($loginData instanceof Workstation && $loginData->offsetExists('authkey')) {
40                \BO\Zmsclient\Auth::setKey($loginData->authkey, time() + \App::SESSION_DURATION);
41
42                if ($wrongModuleResponse = ModuleAccess::rejectWrongModuleAccess(ModuleAccess::MODULE_ADMIN, $loginData, $response)) {
43                    return $wrongModuleResponse;
44                }
45
46                $useraccount = $loginData->getUseraccount();
47                if ($restrictedRoleRedirect = RestrictedRoleRedirect::create($useraccount)) {
48                    return $restrictedRoleRedirect;
49                }
50                return \BO\Slim\Render::redirect('workstationSelect', [], []);
51            }
52            return \BO\Slim\Render::withHtml(
53                $response,
54                'page/index.twig',
55                array(
56                'title' => 'Anmeldung gescheitert',
57                'loginfailed' => true,
58                'workstation' => null,
59                'exception' => $loginData,
60                'showloginform' => true,
61                'oidcproviderlist' => $this->getProviderList($config)
62                )
63            );
64        }
65        if ($workstation instanceof Workstation && $workstation->hasId()) {
66            if ($wrongModuleResponse = ModuleAccess::rejectWrongModuleAccess(ModuleAccess::MODULE_ADMIN, $workstation, $response)) {
67                return $wrongModuleResponse;
68            }
69
70            if ($restrictedRoleRedirect = RestrictedRoleRedirect::create($workstation->getUseraccount())) {
71                return $restrictedRoleRedirect;
72            }
73        }
74
75        return \BO\Slim\Render::withHtml(
76            $response,
77            'page/index.twig',
78            array(
79                'title' => 'Anmeldung',
80                'config' => $config,
81                'workstation' => $workstation,
82                'oidcproviderlist' => $this->getProviderList($config),
83                'oidclogin' => $oidclogin,
84                'showloginform' => (! $oidclogin)
85            )
86        );
87    }
88
89    protected function testLogin($input)
90    {
91        $userAccount = new Useraccount(array(
92            'id' => $input['loginName'],
93            'password' => $input['password'],
94            'departments' => array('id' => 0) // required in schema validation
95        ));
96        try {
97            $workstation = \App::$http->readPostResult('/workstation/login/', $userAccount)->getEntity();
98
99            $sessionHash = hash('sha256', $workstation->authkey);
100            \App::$log->info('Login successful', [
101                'event' => 'auth_login_success',
102                'timestamp' => date('c'),
103                'username' => $userAccount->id,
104                'hashed_session_token' => $sessionHash,
105                'application' => 'zmsadmin'
106            ]);
107
108            return $workstation;
109        } catch (\BO\Zmsclient\Exception $exception) {
110            $template = Helper\TwigExceptionHandler::getExceptionTemplate($exception);
111            if ('BO\Zmsentities\Exception\SchemaValidation' == $exception->template) {
112                $exceptionData = [
113                  'template' => 'exception/bo/zmsbackend/useraccount/exception/invalidcredentials.twig'
114                ];
115                $exceptionData['data']['password']['messages'] = [
116                    'Der Nutzername oder das Passwort wurden falsch eingegeben'
117                ];
118                \App::$log->info('Login failed - invalid credentials', [
119                    'event' => 'auth_login_failed',
120                    'timestamp' => date('c'),
121                    'username' => $userAccount->id,
122                    'error_type' => 'invalid_credentials',
123                    'application' => 'zmsadmin'
124                ]);
125            } elseif ('BO\Zmsbackend\Useraccount\Exception\UserAlreadyLoggedIn' == $exception->template) {
126                \BO\Zmsclient\Auth::setKey($exception->data['authkey'], time() + \App::SESSION_DURATION);
127                \App::$log->info('User already logged in - reusing existing session', [
128                    'event' => 'auth_session_reuse',
129                    'timestamp' => date('c'),
130                    'username' => $userAccount->id,
131                    'hashed_session_token' => hash('sha256', $exception->data['authkey']),
132                    'application' => 'zmsadmin'
133                ]);
134                throw $exception;
135            } elseif (
136                '' != $exception->template
137                && \App::$slim->getContainer()->get('view')->getLoader()->exists($template)
138            ) {
139                $exceptionData = [
140                  'template' => $template,
141                  'data' => $exception->data
142                ];
143                \App::$log->info('Login failed - other error', [
144                    'event' => 'auth_login_failed',
145                    'timestamp' => date('c'),
146                    'username' => $userAccount->id,
147                    'error_type' => 'other',
148                    'error_message' => $exception->getMessage(),
149                    'application' => 'zmsadmin'
150                ]);
151            } else {
152                throw $exception;
153            }
154        }
155        return $exceptionData;
156    }
157
158    protected function getProviderList($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\Zmsadmin\BaseController

21    public function __invoke(RequestInterface $request, ResponseInterface $response, array $args)
22    {
23        $request = $this->initRequest($request);
24        $noCacheResponse = \BO\Slim\Render::withLastModified($response, time(), '0');
25        return $this->readResponse($request, $noCacheResponse, $args);
26    }
41    public function getSchemaConstraintList($schema): array
42    {
43        $list = [];
44        $locale = \App::$language->getLocale();
45        foreach ($schema->properties as $key => $property) {
46            if (isset($property['x-locale'])) {
47                $constraints = $property['x-locale'][$locale];
48                if ($constraints) {
49                    $list[$key]['description'] = $constraints['messages'];
50                }
51            }
52        }
53        return $list;
54    }
65    protected function transformValidationErrors($errorData)
66    {
67        if (!is_array($errorData) && !($errorData instanceof \Traversable)) {
68            return [];
69        }
70        $transformed = [];
71        foreach ($errorData as $pointer => $item) {
72            // Extract field name from JSON pointer (e.g., "/id" -> "id", "/contact/email" -> "contact/email")
73            // If the key doesn't start with "/", it's already a field name, so use it as-is
74            $fieldName = (strpos($pointer, '/') === 0) ? ltrim($pointer, '/') : $pointer;
75            // Handle root level errors
76            if ($fieldName === '' || $fieldName === null) {
77                $fieldName = '_root';
78            }
79            // Ensure the item structure is correct (has 'messages' array)
80            if (is_array($item) && isset($item['messages'])) {
81                $transformed[$fieldName] = $item;
82            } elseif (is_array($item)) {
83                // If item is an array but doesn't have 'messages', wrap it
84                $transformed[$fieldName] = $item;
85            } else {
86                $transformed[$fieldName] = $item;
87            }
88        }
89        return $transformed;
90    }
99    protected function handleEntityWrite(callable $httpCall)
100    {
101        try {
102            return $httpCall();
103        } catch (\BO\Zmsclient\Exception $exception) {
104            if ('BO\Zmsentities\Exception\SchemaValidation' == $exception->template) {
105                return [
106                    'template' => 'exception/bo/zmsentities/exception/schemavalidation.twig',
107                    'include' => true,
108                    'data' => $this->transformValidationErrors($exception->data)
109                ];
110            }
111
112            $template = TwigExceptionHandler::getExceptionTemplate($exception);
113            if (
114                '' != $exception->template
115                && \App::$slim->getContainer()->get('view')->getLoader()->exists($template)
116            ) {
117                return [
118                    'template' => $template,
119                    'include' => true,
120                    'data' => $this->transformValidationErrors($exception->data)
121                ];
122            }
123
124            throw $exception;
125        }
126    }