Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 56
0.00% covered (danger)
0.00%
0 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
RedirectingLoginHookHandler
0.00% covered (danger)
0.00%
0 / 56
0.00% covered (danger)
0.00%
0 / 4
240
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 onPostLoginRedirect
0.00% covered (danger)
0.00%
0 / 43
0.00% covered (danger)
0.00%
0 / 1
132
 onAuthPreserveQueryParams
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
2
 onAuthChangeFormFields
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2/**
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 * http://www.gnu.org/copyleft/gpl.html
17 *
18 * @file
19 */
20
21namespace MediaWiki\Extension\CentralAuth\Hooks\Handlers;
22
23use ErrorPageError;
24use LogicException;
25use MediaWiki\Auth\Hook\AuthPreserveQueryParamsHook;
26use MediaWiki\Context\RequestContext;
27use MediaWiki\Extension\CentralAuth\CentralAuthRedirectingPrimaryAuthenticationProvider;
28use MediaWiki\Extension\CentralAuth\CentralAuthTokenManager;
29use MediaWiki\Extension\CentralAuth\SharedDomainUtils;
30use MediaWiki\Extension\CentralAuth\User\CentralAuthUser;
31use MediaWiki\Hook\PostLoginRedirectHook;
32use MediaWiki\Logger\LoggerFactory;
33use MediaWiki\SpecialPage\Hook\AuthChangeFormFieldsHook;
34
35/**
36 * Hook handler for hooks related to SUL3 login.
37 *
38 * @see CentralAuthRedirectingPrimaryAuthenticationProvider
39 */
40class RedirectingLoginHookHandler implements
41    PostLoginRedirectHook,
42    AuthPreserveQueryParamsHook,
43    AuthChangeFormFieldsHook
44{
45
46    private CentralAuthTokenManager $tokenManager;
47    private SharedDomainUtils $sharedDomainUtils;
48
49    public function __construct(
50        CentralAuthTokenManager $tokenManager,
51        SharedDomainUtils $sharedDomainUtils
52    ) {
53        $this->tokenManager = $tokenManager;
54        $this->sharedDomainUtils = $sharedDomainUtils;
55    }
56
57    /**
58     * After a SUL3 login on the shared login domain, redirect the user back to the
59     * original wiki and indicate the result of the login.
60     * @inheritDoc
61     */
62    public function onPostLoginRedirect( &$returnTo, &$returnToQuery, &$type ) {
63        $context = RequestContext::getMain();
64        $request = $context->getRequest();
65        if (
66            !$this->sharedDomainUtils->isSharedDomain()
67            || !$this->sharedDomainUtils->isSul3Enabled( $request )
68        ) {
69            return;
70        }
71
72        $token = $request->getRawVal( 'centralauthLoginToken' );
73        $inputData = null;
74        if ( $token ) {
75            $inputData = $this->tokenManager->detokenizeAndDelete(
76                $token,
77                CentralAuthRedirectingPrimaryAuthenticationProvider::START_TOKEN_KEY_PREFIX
78            );
79        }
80        if ( !$inputData ) {
81            LoggerFactory::getInstance( 'authevents' )
82                ->warning( 'Authentication request with bad token', [
83                    'event' => ( $type === 'signup' ) ? 'accountcreation' : 'login',
84                    'successful' => false,
85                    'extension' => 'CentralAuth',
86                    'accountType' => $context->getUser()->isNamed() ? 'named' : 'temp',
87                    'status' => 'badtoken'
88                ] );
89            throw new ErrorPageError( 'centralauth-error-badtoken', 'centralauth-error-badtoken' );
90        }
91        $returnUrl = $inputData['returnUrl'];
92
93        if ( !$context->getUser()->isRegistered() ) {
94            throw new LogicException( 'Unregistered user at end of login' );
95        }
96        $centralUser = CentralAuthUser::getInstanceByName( $context->getUser()->getName() );
97        if ( !$centralUser->exists() ) {
98            $centralUser = CentralAuthUser::getPrimaryInstanceByName( $context->getUser()->getName() );
99        }
100        if ( !$centralUser->exists() || !$centralUser->isAttached() ) {
101            throw new LogicException( 'Unattached user at end of login' );
102        }
103
104        $outputData = $inputData + [
105            'username' => $centralUser->getName(),
106            'userId' => $centralUser->getId(),
107            'rememberMe' => $request->getSession()->shouldRememberUser(),
108        ];
109        $token = $this->tokenManager->tokenize(
110            $outputData,
111            CentralAuthRedirectingPrimaryAuthenticationProvider::COMPLETE_TOKEN_KEY_PREFIX
112        );
113
114        // We have a return URL from the login wiki, we're fine.
115        $url = wfAppendQuery( $returnUrl, [ 'centralauthLoginToken' => $token ] );
116        $context->getOutput()->redirect( $url );
117        $type = 'success';
118
119        return true;
120    }
121
122    public function onAuthPreserveQueryParams( array &$params, array $options ) {
123        $context = RequestContext::getMain();
124        $request = $context->getRequest();
125
126        $params += [
127            'centralauthLoginToken' => $request->getRawVal( 'centralauthLoginToken' ),
128            'wikiid' => $request->getRawVal( 'wikiid' ),
129            'usesul3' => $request->getRawVal( 'usesul3' ),
130            'useformat' => $request->getRawVal( 'useformat' ),
131        ];
132    }
133
134    /**
135     * Move the redirect button below the normal login button. Displaying the normal login form
136     * is prevented elsewhere, but just in case, if that fails, avoid this button being the
137     * default form control that gets submitted when the user types into the username/password
138     * field and presses Enter.
139     * @inheritDoc
140     */
141    public function onAuthChangeFormFields( $requests, $fieldInfo, &$formDescriptor, $action ) {
142        $formFieldName = CentralAuthRedirectingPrimaryAuthenticationProvider::NON_LOGIN_WIKI_BUTTONREQUEST_NAME;
143        if ( isset( $formDescriptor[$formFieldName] ) ) {
144            $formDescriptor[$formFieldName]['weight'] = 101;
145        }
146    }
147}