Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 14 |
|
0.00% |
0 / 4 |
CRAP | |
0.00% |
0 / 1 |
FilteredRequestTracker | |
0.00% |
0 / 14 |
|
0.00% |
0 / 4 |
156 | |
0.00% |
0 / 1 |
markRequestAsFiltered | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
saveState | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
30 | |||
isCurrentAuthenticationFlowFiltered | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
20 | |||
reset | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 |
1 | <?php |
2 | |
3 | namespace MediaWiki\Extension\CentralAuth; |
4 | |
5 | use LogicException; |
6 | use MediaWiki\Auth\AuthManager; |
7 | use MediaWiki\Request\WebRequest; |
8 | use MWExceptionHandler; |
9 | |
10 | /** |
11 | * A trivial service to track whether authentication providers have been filtered in the current |
12 | * request, used as shared state between SharedDomainHookHandler (which does the filtering, and |
13 | * needs to know at the end of the authentication whether filtering was done, but can't easily |
14 | * preserve that information through a multi-request authentication process) and |
15 | * CentralAuthSharedDomainPreAuthenticationProvider (which can use AuthManager to preserve state |
16 | * during an authentication flow). |
17 | * @see \MediaWiki\Extension\CentralAuth\Hooks\Handlers\SharedDomainHookHandler |
18 | * @see \MediaWiki\Extension\CentralAuth\CentralAuthSharedDomainPreAuthenticationProvider |
19 | */ |
20 | class FilteredRequestTracker { |
21 | |
22 | private const SESSION_KEY = 'CentralAuth.filtered'; |
23 | |
24 | /** @var WebRequest|null The authentication request for which we disabled non-SUL3 local providers. */ |
25 | private ?WebRequest $filteredRequest = null; |
26 | |
27 | /** |
28 | * Mark the given request as using relaxed security due to the AuthManagerVerifyAuthentication hook. |
29 | * Note that on authentication requests this is called before the authentication process starts, |
30 | * and it can be called on non-authentication requests as well (initializing authentication |
31 | * providers is involved in e.g. skin logic). |
32 | */ |
33 | public function markRequestAsFiltered( WebRequest $request ): void { |
34 | $this->filteredRequest = $request; |
35 | } |
36 | |
37 | /** |
38 | * Store filtered state in the authentication session. Must be invoked inside the |
39 | * authentication flow. |
40 | * @param AuthManager $authManager |
41 | */ |
42 | public function saveState( AuthManager $authManager ): void { |
43 | $arePreviousRequestsFiltered = $authManager->getAuthenticationSessionData( self::SESSION_KEY, false ); |
44 | if ( !$arePreviousRequestsFiltered && $this->filteredRequest ) { |
45 | $authManager->setAuthenticationSessionData( self::SESSION_KEY, true ); |
46 | } elseif ( $arePreviousRequestsFiltered && !$this->filteredRequest ) { |
47 | // TODO This is probably possible if the user manipulates request data mid-authentication; |
48 | // maybe we should prevent that in a cleaner way. For now just log. The session flag |
49 | // will stick, so security-wise the AuthManagerVerifyAuthentication hook will |
50 | // handle it correctly. |
51 | MWExceptionHandler::logException( new LogicException( 'Filter flag applied inconsistently' ) ); |
52 | } |
53 | } |
54 | |
55 | /** |
56 | * Check whether the current authentication flow is using relaxed security due to the |
57 | * AuthManagerVerifyAuthentication hook (invoked in either the current request or a subsequent |
58 | * authentication step). |
59 | */ |
60 | public function isCurrentAuthenticationFlowFiltered( AuthManager $authManager ): bool { |
61 | $arePreviousRequestsFiltered = $authManager->getAuthenticationSessionData( self::SESSION_KEY, false ); |
62 | if ( $this->filteredRequest && $authManager->getRequest() !== $this->filteredRequest ) { |
63 | // Maybe a unit test where some but not all services got reset? |
64 | // Should not happen during real authentication. |
65 | MWExceptionHandler::logException( new LogicException( |
66 | 'Request changed between AuthManagerFilterProviders and AuthManagerVerifyAuthentication' ) ); |
67 | } |
68 | return $this->filteredRequest || $arePreviousRequestsFiltered; |
69 | } |
70 | |
71 | /** |
72 | * Reset the state of the tracker. |
73 | * @internal For use in tests only |
74 | */ |
75 | public function reset(): void { |
76 | if ( !defined( 'MW_PHPUNIT_TEST' ) ) { |
77 | throw new LogicException( 'reset() is for testing only' ); |
78 | } |
79 | $this->filteredRequest = null; |
80 | } |
81 | |
82 | } |