Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 60 |
|
0.00% |
0 / 12 |
CRAP | |
0.00% |
0 / 1 |
CentralAuthRedirectingPrimaryAuthenticationProvider | |
0.00% |
0 / 60 |
|
0.00% |
0 / 12 |
462 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
2 | |||
getAuthenticationRequests | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
20 | |||
beginPrimaryAuthentication | |
0.00% |
0 / 16 |
|
0.00% |
0 / 1 |
6 | |||
continuePrimaryAuthentication | |
0.00% |
0 / 15 |
|
0.00% |
0 / 1 |
12 | |||
testUserCanAuthenticate | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
providerNormalizeUsername | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
testUserExists | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
20 | |||
providerAllowsAuthenticationDataChange | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
providerChangeAuthenticationData | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
accountCreationType | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
beginPrimaryAccountCreation | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getCentralLoginUrl | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | |
3 | namespace MediaWiki\Extension\CentralAuth; |
4 | |
5 | use IDBAccessObject; |
6 | use LogicException; |
7 | use MediaWiki\Auth\AbstractPrimaryAuthenticationProvider; |
8 | use MediaWiki\Auth\AuthenticationRequest; |
9 | use MediaWiki\Auth\AuthenticationResponse; |
10 | use MediaWiki\Auth\AuthManager; |
11 | use MediaWiki\Extension\CentralAuth\Hooks\Handlers\RedirectingLoginHookHandler; |
12 | use MediaWiki\Extension\CentralAuth\User\CentralAuthUser; |
13 | use MediaWiki\Title\TitleFactory; |
14 | use MediaWiki\User\UserNameUtils; |
15 | use MediaWiki\WikiMap\WikiMap; |
16 | use RuntimeException; |
17 | use StatusValue; |
18 | |
19 | /** |
20 | * Redirect-based provider which sends the user to another domain, assumed to be |
21 | * served by the same wiki farm, to log in, and expects to receive the result of |
22 | * that authentication process when the user returns. |
23 | */ |
24 | class CentralAuthRedirectingPrimaryAuthenticationProvider |
25 | extends AbstractPrimaryAuthenticationProvider |
26 | { |
27 | public const NON_LOGIN_WIKI_BUTTONREQUEST_NAME = 'non-loginwiki'; |
28 | |
29 | /** |
30 | * @internal |
31 | * @var string The storage key prefix for the URL token used for continuing |
32 | * authentication in the central login wiki. |
33 | */ |
34 | public const RETURN_URL_TOKEN_KEY_PREFIX = 'centralauth-homewiki-return-url-token'; |
35 | |
36 | private TitleFactory $titleFactory; |
37 | private CentralAuthSessionManager $sessionManager; |
38 | private CentralAuthUtilityService $centralAuthUtility; |
39 | private SharedDomainUtils $sharedDomainUtils; |
40 | |
41 | public function __construct( |
42 | TitleFactory $titleFactory, |
43 | CentralAuthSessionManager $sessionManager, |
44 | CentralAuthUtilityService $centralAuthUtility, |
45 | SharedDomainUtils $sharedDomainUtils |
46 | ) { |
47 | $this->titleFactory = $titleFactory; |
48 | $this->sessionManager = $sessionManager; |
49 | $this->centralAuthUtility = $centralAuthUtility; |
50 | $this->sharedDomainUtils = $sharedDomainUtils; |
51 | } |
52 | |
53 | /** @inheritDoc */ |
54 | public function getAuthenticationRequests( $action, array $options ) { |
55 | if ( $action === AuthManager::ACTION_LOGIN |
56 | && $this->sharedDomainUtils->isSul3Enabled( $this->manager->getRequest() ) |
57 | && !$this->sharedDomainUtils->isSharedDomain() |
58 | ) { |
59 | return [ new CentralAuthRedirectingAuthenticationRequest() ]; |
60 | } |
61 | return []; |
62 | } |
63 | |
64 | /** @inheritDoc */ |
65 | public function beginPrimaryAuthentication( array $reqs ) { |
66 | $req = CentralAuthRedirectingAuthenticationRequest::getRequestByName( |
67 | $reqs, |
68 | self::NON_LOGIN_WIKI_BUTTONREQUEST_NAME |
69 | ); |
70 | |
71 | if ( !$req ) { |
72 | return AuthenticationResponse::newAbstain(); |
73 | } |
74 | |
75 | $this->sharedDomainUtils->assertSul3Enabled( $this->manager->getRequest() ); |
76 | $this->sharedDomainUtils->assertIsNotSharedDomain(); |
77 | |
78 | $returnUrlToken = $this->centralAuthUtility->tokenize( |
79 | $req->returnToUrl, self::RETURN_URL_TOKEN_KEY_PREFIX, $this->sessionManager |
80 | ); |
81 | |
82 | $url = wfAppendQuery( |
83 | $this->getCentralLoginUrl(), |
84 | [ 'returnUrlToken' => $returnUrlToken ] |
85 | ); |
86 | return AuthenticationResponse::newRedirect( [ new CentralAuthReturnRequest() ], $url ); |
87 | } |
88 | |
89 | /** @inheritDoc */ |
90 | public function continuePrimaryAuthentication( array $reqs ) { |
91 | $this->sharedDomainUtils->assertSul3Enabled( $this->manager->getRequest() ); |
92 | $this->sharedDomainUtils->assertIsNotSharedDomain(); |
93 | |
94 | $req = AuthenticationRequest::getRequestByClass( |
95 | $reqs, CentralAuthReturnRequest::class |
96 | ); |
97 | |
98 | if ( !$req ) { |
99 | throw new LogicException( 'Local authentication failed, please try again.' ); |
100 | } |
101 | |
102 | $username = $this->centralAuthUtility->detokenize( |
103 | $req->token, |
104 | RedirectingLoginHookHandler::LOGIN_CONTINUE_USERNAME_KEY_PREFIX, |
105 | $this->sessionManager |
106 | ); |
107 | |
108 | if ( !$username ) { |
109 | throw new RuntimeException( 'Invalid user token, try to login again' ); |
110 | } |
111 | |
112 | return AuthenticationResponse::newPass( $username ); |
113 | } |
114 | |
115 | /** @inheritDoc */ |
116 | public function testUserCanAuthenticate( $username ) { |
117 | return false; |
118 | } |
119 | |
120 | /** @inheritDoc */ |
121 | public function providerNormalizeUsername( $username ) { |
122 | return null; |
123 | } |
124 | |
125 | /** @inheritDoc */ |
126 | public function testUserExists( $username, $flags = IDBAccessObject::READ_NORMAL ) { |
127 | if ( $this->sharedDomainUtils->isSharedDomain() ) { |
128 | return false; |
129 | } |
130 | |
131 | $username = $this->userNameUtils->getCanonical( $username, UserNameUtils::RIGOR_USABLE ); |
132 | if ( $username === false ) { |
133 | return false; |
134 | } |
135 | |
136 | $centralUser = CentralAuthUser::getInstanceByName( $username ); |
137 | return $centralUser && $centralUser->exists(); |
138 | } |
139 | |
140 | /** @inheritDoc */ |
141 | public function providerAllowsAuthenticationDataChange( AuthenticationRequest $req, $checkData = true ) { |
142 | return StatusValue::newGood( 'ignored' ); |
143 | } |
144 | |
145 | /** @inheritDoc */ |
146 | public function providerChangeAuthenticationData( AuthenticationRequest $req ) { |
147 | } |
148 | |
149 | /** @inheritDoc */ |
150 | public function accountCreationType() { |
151 | return self::TYPE_NONE; |
152 | } |
153 | |
154 | /** @inheritDoc */ |
155 | public function beginPrimaryAccountCreation( $user, $creator, array $reqs ) { |
156 | return AuthenticationResponse::newAbstain(); |
157 | } |
158 | |
159 | /** |
160 | * Get the login URL on the shared login domain wiki. |
161 | * |
162 | * @return string |
163 | */ |
164 | private function getCentralLoginUrl(): string { |
165 | $localUrl = $this->titleFactory->newFromText( 'Special:UserLogin' )->getLocalURL(); |
166 | $url = $this->config->get( 'CentralAuthSsoUrlPrefix' ) . $localUrl; |
167 | |
168 | return wfAppendQuery( $url, [ |
169 | // At this point, we should just be leaving the local |
170 | // wiki before hitting the loginwiki. |
171 | 'wikiid' => WikiMap::getCurrentWikiId(), |
172 | // TODO: Fix T369467 |
173 | 'returnto' => 'Main_Page', |
174 | 'usesul3' => '1', |
175 | ] ); |
176 | } |
177 | |
178 | } |