Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
87.64% |
78 / 89 |
|
20.00% |
1 / 5 |
CRAP | |
0.00% |
0 / 1 |
SessionController | |
87.64% |
78 / 89 |
|
20.00% |
1 / 5 |
30.59 | |
0.00% |
0 / 1 |
putSessionAdmin | n/a |
0 / 0 |
n/a |
0 / 0 |
5 | |||||
putSessionLogin | |
91.67% |
22 / 24 |
|
0.00% |
0 / 1 |
5.01 | |||
putSessionPerson | n/a |
0 / 0 |
n/a |
0 / 0 |
1 | |||||
registerDependantSessions | |
93.75% |
30 / 32 |
|
0.00% |
0 / 1 |
9.02 | |||
getWorkspace | |
66.67% |
2 / 3 |
|
0.00% |
0 / 1 |
2.15 | |||
getSession | |
100.00% |
24 / 24 |
|
100.00% |
1 / 1 |
4 | |||
deleteSession | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
12 |
1 | <?php |
2 | |
3 | /** @noinspection PhpUnhandledExceptionInspection */ |
4 | declare(strict_types=1); |
5 | |
6 | // TODO unit tests ! |
7 | |
8 | use Slim\Exception\HttpBadRequestException; |
9 | use Slim\Exception\HttpException; |
10 | use Slim\Exception\HttpUnauthorizedException; |
11 | use Slim\Http\Response; |
12 | use Slim\Http\ServerRequest as Request; |
13 | |
14 | class SessionController extends Controller { |
15 | protected static array $_workspaces = []; |
16 | |
17 | /** |
18 | * @codeCoverageIgnore |
19 | */ |
20 | public static function putSessionAdmin(Request $request, Response $response): Response { |
21 | usleep(500000); // 0.5s delay to slow down brute force attack |
22 | |
23 | $body = RequestBodyParser::getElementsFromRequest($request, [ |
24 | "name" => 'REQUIRED', |
25 | "password" => 'REQUIRED' |
26 | ]); |
27 | |
28 | $attempts = CacheService::getFailedLogins($body['name']); |
29 | if ($attempts >= 5) { |
30 | throw new HttpError("Too many login attempts", 429); |
31 | } |
32 | |
33 | $token = self::adminDAO()->createAdminToken($body['name'], $body['password']); |
34 | |
35 | if (is_a($token, FailedLogin::class)) { |
36 | CacheService::addFailedLogin($body['name']); |
37 | throw new HttpError("No login with this password.", 400); |
38 | } |
39 | |
40 | $admin = self::adminDAO()->getAdmin($token); |
41 | $workspaces = self::adminDAO()->getWorkspaces($token); |
42 | $accessSet = AccessSet::createFromAdminToken($admin, ...$workspaces); |
43 | |
44 | if (!$accessSet->hasAccessType(AccessObjectType::WORKSPACE_ADMIN) and !$accessSet->hasAccessType(AccessObjectType::SUPER_ADMIN)) { |
45 | throw new HttpException($request, "You don't have any workspaces and are not allowed to create some.", 204); |
46 | } |
47 | |
48 | return $response->withJson($accessSet); |
49 | } |
50 | |
51 | public static function putSessionLogin(Request $request, Response $response): Response { |
52 | $body = RequestBodyParser::getElementsFromRequest($request, [ |
53 | "name" => 'REQUIRED', |
54 | "password" => '' |
55 | ]); |
56 | |
57 | $attempts = CacheService::getFailedLogins($body['name']); |
58 | if ($attempts >= 5) { |
59 | throw new HttpError("Too many login attempts", 429); |
60 | } |
61 | |
62 | $loginSession = self::sessionDAO()->getOrCreateLoginSession($body['name'], $body['password']); |
63 | |
64 | if (!is_a($loginSession, LoginSession::class)) { |
65 | if ($loginSession === FailedLogin::wrongPasswordProtectedLogin) { |
66 | CacheService::addFailedLogin($body['name']); |
67 | } |
68 | $userName = htmlspecialchars($body['name']); |
69 | throw new HttpBadRequestException($request, "No Login for `$userName` with this password."); |
70 | } |
71 | |
72 | if (!$loginSession->getLogin()->isCodeRequired()) { |
73 | $personSession = self::sessionDAO()->createOrUpdatePersonSession($loginSession, ''); |
74 | |
75 | CacheService::removeAuthentication($personSession); |
76 | |
77 | $testsOfPerson = self::sessionDAO()->getTestsOfPerson($personSession); |
78 | $groupMonitors = self::sessionDAO()->getGroupMonitors($personSession); |
79 | $sysChecks = self::sessionDAO()->getSysChecksOfPerson($personSession); |
80 | $accessSet = AccessSet::createFromPersonSession($personSession, ...$testsOfPerson, ...$groupMonitors, ...$sysChecks); |
81 | |
82 | self::registerDependantSessions($loginSession); |
83 | CacheService::storeAuthentication($personSession); |
84 | |
85 | } else { |
86 | $accessSet = AccessSet::createFromLoginSession($loginSession); |
87 | } |
88 | |
89 | return $response->withJson($accessSet); |
90 | } |
91 | |
92 | /** |
93 | * @codeCoverageIgnore |
94 | */ |
95 | public static function putSessionPerson(Request $request, Response $response): Response { |
96 | $body = RequestBodyParser::getElementsFromRequest($request, [ |
97 | 'code' => '' |
98 | ]); |
99 | |
100 | $loginSession = self::sessionDAO()->getLoginSessionByToken(self::authToken($request)->getToken()); |
101 | |
102 | $personSession = self::sessionDAO()->createOrUpdatePersonSession($loginSession, $body['code']); |
103 | CacheService::removeAuthentication($personSession); |
104 | $testsOfPerson = self::sessionDAO()->getTestsOfPerson($personSession); |
105 | CacheService::storeAuthentication($personSession); |
106 | return $response->withJson(AccessSet::createFromPersonSession($personSession, ...$testsOfPerson)); |
107 | } |
108 | |
109 | private static function registerDependantSessions(LoginSession $login): void { |
110 | $members = self::sessionDAO()->getDependantSessions($login); |
111 | |
112 | $workspace = self::getWorkspace($login->getLogin()->getWorkspaceId()); |
113 | $bookletFiles = []; |
114 | |
115 | foreach ($members as $member) { |
116 | /* @var $member LoginSession */ |
117 | |
118 | if (Mode::hasCapability($member->getLogin()->getMode(), 'alwaysNewSession')) { |
119 | continue; |
120 | } |
121 | |
122 | if (!Mode::hasCapability($member->getLogin()->getMode(), 'monitorable')) { |
123 | continue; |
124 | } |
125 | |
126 | if (!$member->getToken()) { |
127 | $member = SessionController::sessionDAO()->createLoginSession($member->getLogin()); |
128 | } |
129 | |
130 | foreach ($member->getLogin()->getBooklets() as $code => $booklets) { |
131 | $memberPersonSession = SessionController::sessionDAO()->createOrUpdatePersonSession( |
132 | $member, |
133 | $code, |
134 | true, |
135 | false |
136 | ); |
137 | |
138 | foreach ($booklets as $bookletId) { |
139 | if (!isset($bookletFiles[$bookletId])) { |
140 | $bookletFile = $workspace->getFileById('Booklet', $bookletId); |
141 | $bookletFiles[$bookletId] = $bookletFile; |
142 | } else { |
143 | $bookletFile = $bookletFiles[$bookletId]; |
144 | } |
145 | /* @var $bookletFile XMLFileBooklet */ |
146 | |
147 | $test = self::testDAO()->getTestByPerson($memberPersonSession->getPerson()->getId(), $bookletId); |
148 | if (!$test) { |
149 | $test = self::testDAO()->createTest( |
150 | $memberPersonSession->getPerson()->getId(), |
151 | $bookletId, |
152 | $bookletFile->getLabel() |
153 | ); |
154 | } |
155 | |
156 | $sessionMessage = SessionChangeMessage::session($test->id, $memberPersonSession); |
157 | $sessionMessage->setTestState([], $bookletId); |
158 | BroadcastService::sessionChange($sessionMessage); |
159 | } |
160 | } |
161 | } |
162 | } |
163 | |
164 | private static function getWorkspace(int $workspaceId): Workspace { |
165 | if (!isset(self::$_workspaces[$workspaceId])) { |
166 | self::$_workspaces[$workspaceId] = new Workspace($workspaceId); |
167 | } |
168 | |
169 | return self::$_workspaces[$workspaceId]; |
170 | } |
171 | |
172 | public static function getSession(Request $request, Response $response): Response { |
173 | $authToken = self::authToken($request); |
174 | |
175 | if ($authToken->getType() == "login") { |
176 | $loginSession = self::sessionDAO()->getLoginSessionByToken($authToken->getToken()); |
177 | return $response->withJson(AccessSet::createFromLoginSession($loginSession)); |
178 | } |
179 | |
180 | if ($authToken->getType() == "person") { |
181 | $personSession = self::sessionDAO()->getPersonSessionByToken($authToken->getToken()); |
182 | $testsOfPerson = self::sessionDAO()->getTestsOfPerson($personSession); |
183 | $workspace = self::workspaceDAO($personSession->getLoginSession()->getLogin()->getWorkspaceId()); |
184 | $workspaceData = new WorkspaceData( |
185 | $workspace->getWorkspaceId(), |
186 | $workspace->getWorkspaceName(), |
187 | 'R' |
188 | ); |
189 | $groupMonitors = self::sessionDAO()->getGroupMonitors($personSession); |
190 | $sysChecks = self::sessionDAO()->getSysChecksOfPerson($personSession); |
191 | |
192 | $accessSet = AccessSet::createFromPersonSession($personSession, $workspaceData, ...$testsOfPerson, ...$groupMonitors, ...$sysChecks); |
193 | return $response->withJson($accessSet); |
194 | } |
195 | |
196 | if ($authToken->getType() == "admin") { |
197 | $admin = self::adminDAO()->getAdmin($authToken->getToken()); |
198 | $workspaces = self::adminDAO()->getWorkspaces($authToken->getToken()); |
199 | $accessSet = AccessSet::createFromAdminToken($admin, ...$workspaces); |
200 | self::adminDAO()->refreshAdminToken($authToken->getToken()); |
201 | return $response->withJson($accessSet); |
202 | } |
203 | |
204 | throw new HttpUnauthorizedException($request); |
205 | } |
206 | |
207 | public static function deleteSession(Request $request, Response $response): Response { |
208 | $authToken = self::authToken($request); |
209 | |
210 | if ($authToken->getType() == "person") { |
211 | self::sessionDAO()->deletePersonToken($authToken); |
212 | } |
213 | |
214 | if ($authToken->getType() == "admin") { |
215 | self::adminDAO()->deleteAdminSession($authToken); |
216 | } |
217 | |
218 | // nothing to do for login-sessions; they have constant token as they are only the first step of 2f-auth |
219 | return $response->withStatus(205); |
220 | } |
221 | |
222 | } |