Passed
Push — main ( ee4532...c369f5 )
by MusikAnimal
11:09
created

DefaultController::loginAction()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 14
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 6
c 1
b 0
f 0
nc 2
nop 2
dl 0
loc 14
ccs 0
cts 6
cp 0
crap 6
rs 10
1
<?php
2
/**
3
 * This file contains only the DefaultController class.
4
 */
5
6
declare(strict_types=1);
7
8
namespace AppBundle\Controller;
9
10
use AppBundle\Model\Edit;
11
use AppBundle\Repository\ProjectRepository;
12
use MediaWiki\OAuthClient\Client;
13
use MediaWiki\OAuthClient\ClientConfig;
14
use MediaWiki\OAuthClient\Consumer;
15
use MediaWiki\OAuthClient\Exception;
16
use MediaWiki\OAuthClient\Token;
17
use Symfony\Component\HttpFoundation\JsonResponse;
18
use Symfony\Component\HttpFoundation\RedirectResponse;
19
use Symfony\Component\HttpFoundation\Request;
20
use Symfony\Component\HttpFoundation\Response;
21
use Symfony\Component\HttpFoundation\Session\SessionInterface;
22
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
23
use Symfony\Component\Routing\Annotation\Route;
24
25
/**
26
 * The DefaultController handles the homepage, about pages, and user authentication.
27
 */
28
class DefaultController extends XtoolsController
29
{
30
    /** @var Client The Oauth HTTP client. */
31
    protected $oauthClient;
32
33
    /**
34
     * Required to be defined by XtoolsController, though here it is unused.
35
     * @return string
36
     * @codeCoverageIgnore
37
     */
38
    public function getIndexRoute(): string
39
    {
40
        return 'homepage';
41
    }
42
43
    /**
44
     * Display the homepage.
45
     * @Route("/", name="homepage")
46
     * @Route("/index.php", name="homepageIndexPhp")
47
     * @return Response
48
     */
49 1
    public function indexAction(): Response
50
    {
51 1
        return $this->render('default/index.html.twig', [
52 1
            'xtPage' => 'home',
53
        ]);
54
    }
55
56
    /**
57
     * Display some configuration details, when in development mode.
58
     * @Route("/config", name="configPage")
59
     * @return Response
60
     * @codeCoverageIgnore
61
     */
62
    public function configAction(): Response
63
    {
64
65
        if ('dev' !== $this->container->getParameter('kernel.environment')) {
66
            throw new NotFoundHttpException();
67
        }
68
69
        $params = $this->container->getParameterBag()->all();
0 ignored issues
show
Bug introduced by
The method getParameterBag() does not exist on Symfony\Component\Depend...tion\ContainerInterface. Did you maybe mean getParameter()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

69
        $params = $this->container->/** @scrutinizer ignore-call */ getParameterBag()->all();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
70
71
        foreach (array_keys($params) as $key) {
72
            if (false !== strpos($key, 'password')) {
73
                $params[$key] = '<REDACTED>';
74
            }
75
        }
76
77
        // replace this example code with whatever you need
78
        return $this->render('default/config.html.twig', [
79
            'xtTitle' => 'Config',
80
            'xtPageTitle' => 'Config',
81
            'xtPage' => 'index',
82
            'dump' => print_r($params, true),
83
        ]);
84
    }
85
86
    /**
87
     * Redirect to the default project (or Meta) for Oauth authentication.
88
     * @Route("/login", name="login")
89
     * @param Request $request
90
     * @param SessionInterface $session
91
     * @return RedirectResponse
92
     * @throws Exception If initialization fails.
93
     */
94
    public function loginAction(Request $request, SessionInterface $session): RedirectResponse
95
    {
96
        try {
97
            [ $next, $token ] = $this->getOauthClient($request)->initiate();
98
        } catch (Exception $oauthException) {
99
            throw $oauthException;
100
            // @TODO Make this work.
101
            //$this->addFlash('error', $oauthException->getMessage());
102
            //return $this->redirectToRoute('homepage');
103
        }
104
105
        // Save the request token to the session.
106
        $session->set('oauth_request_token', $token);
107
        return new RedirectResponse($next);
108
    }
109
110
    /**
111
     * Receive authentication credentials back from the Oauth wiki.
112
     * @Route("/oauth_callback", name="oauth_callback")
113
     * @Route("/oauthredirector.php", name="old_oauth_callback")
114
     * @param Request $request The HTTP request.
115
     * @param SessionInterface $session
116
     * @return RedirectResponse
117
     */
118 1
    public function oauthCallbackAction(Request $request, SessionInterface $session): RedirectResponse
119
    {
120
        // Give up if the required GET params don't exist.
121 1
        if (!$request->get('oauth_verifier')) {
122 1
            throw $this->createNotFoundException('No OAuth verifier given.');
123
        }
124
125
        // Complete authentication.
126
        $client = $this->getOauthClient();
127
        $token = $session->get('oauth_request_token');
128
129
        if (!is_a($token, Token::class)) {
130
            $this->addFlashMessage('notice', 'error-login');
131
            return $this->redirectToRoute('homepage');
132
        }
133
134
        $verifier = $request->get('oauth_verifier');
135
        $accessToken = $client->complete($token, $verifier);
136
137
        // Store access token, and remove request token.
138
        $session->set('oauth_access_token', $accessToken);
139
        $session->remove('oauth_request_token');
140
141
        // Store user identity.
142
        $ident = $client->identify($accessToken);
143
        $session->set('logged_in_user', $ident);
144
145
        // Store reference to the client.
146
        $session->set('oauth_client', $this->oauthClient);
147
148
        // Redirect to callback, if given.
149
        if ($request->query->get('redirect')) {
150
            return $this->redirect($request->query->get('redirect'));
151
        }
152
153
        // Send back to homepage.
154
        return $this->redirectToRoute('homepage');
155
    }
156
157
    /**
158
     * Get an OAuth client, configured to the default project.
159
     * (This shouldn't really be in this class, but oh well.)
160
     * @param Request|null $request
161
     * @return Client
162
     * @codeCoverageIgnore
163
     */
164
    protected function getOauthClient(?Request $request = null): Client
165
    {
166
        if ($this->oauthClient instanceof Client) {
0 ignored issues
show
introduced by
$this->oauthClient is always a sub-type of MediaWiki\OAuthClient\Client.
Loading history...
167
            return $this->oauthClient;
168
        }
169
        $defaultProject = ProjectRepository::getProject(
170
            $this->getParameter('central_auth_project'),
171
            $this->container
172
        );
173
        $endpoint = $defaultProject->getUrl(false)
174
                    . $defaultProject->getScript()
175
                    . '?title=Special:OAuth';
176
        $conf = new ClientConfig($endpoint);
177
        $consumerKey = $this->getParameter('oauth_key');
178
        $consumerSecret =  $this->getParameter('oauth_secret');
179
        $conf->setConsumer(new Consumer($consumerKey, $consumerSecret));
180
        $this->oauthClient = new Client($conf);
181
182
        // Set the callback URL if given. Used to redirect back to target page after logging in.
183
        if ($request && $request->query->get('callback')) {
184
            $this->oauthClient->setCallback($request->query->get('callback'));
185
        }
186
187
        return $this->oauthClient;
188
    }
189
190
    /**
191
     * Log out the user and return to the homepage.
192
     * @Route("/logout", name="logout")
193
     * @return RedirectResponse
194
     */
195 1
    public function logoutAction(): RedirectResponse
196
    {
197 1
        $this->get('session')->invalidate();
198 1
        return $this->redirectToRoute('homepage');
199
    }
200
201
    /************************ API endpoints ************************/
202
203
    /**
204
     * Get domain name, URL, and API URL of the given project.
205
     * @Route("/api/project/normalize/{project}", name="ProjectApiNormalize")
206
     * @return JsonResponse
207
     */
208
    public function normalizeProjectApiAction(): JsonResponse
209
    {
210
        return $this->getFormattedApiResponse([
211
            'domain' => $this->project->getDomain(),
212
            'url' => $this->project->getUrl(),
213
            'api' => $this->project->getApiUrl(),
214
            'database' => $this->project->getDatabaseName(),
215
        ]);
216
    }
217
218
    /**
219
     * Get all namespaces of the given project. This endpoint also does the same thing
220
     * as the /project/normalize endpoint, returning other basic info about the project.
221
     * @Route("/api/project/namespaces/{project}", name="ProjectApiNamespaces")
222
     * @return JsonResponse
223
     */
224 1
    public function namespacesApiAction(): JsonResponse
225
    {
226 1
        return $this->getFormattedApiResponse([
227 1
            'domain' => $this->project->getDomain(),
228 1
            'url' => $this->project->getUrl(),
229 1
            'api' => $this->project->getApiUrl(),
230 1
            'database' => $this->project->getDatabaseName(),
231 1
            'namespaces' => $this->project->getNamespaces(),
232
        ]);
233
    }
234
235
    /**
236
     * Get assessment data for a given project.
237
     * @Route("/api/project/assessments/{project}", name="ProjectApiAssessments")
238
     * @return JsonResponse
239
     */
240 1
    public function projectAssessmentsApiAction(): JsonResponse
241
    {
242 1
        return $this->getFormattedApiResponse([
243 1
            'project' => $this->project->getDomain(),
244 1
            'assessments' => $this->project->getPageAssessments()->getConfig(),
245
        ]);
246
    }
247
248
    /**
249
     * Get assessment data for all projects.
250
     * @Route("/api/project/assessments", name="ApiAssessmentsConfig")
251
     * @return JsonResponse
252
     */
253
    public function assessmentsConfigApiAction(): JsonResponse
254
    {
255
        // Here there is no Project, so we don't use XtoolsController::getFormattedApiResponse().
256
        $response = new JsonResponse();
257
        $response->setEncodingOptions(JSON_NUMERIC_CHECK);
258
        $response->setStatusCode(Response::HTTP_OK);
259
        $response->setData([
260
            'projects' => array_keys($this->container->getParameter('assessments')),
0 ignored issues
show
Bug introduced by
It seems like $this->container->getParameter('assessments') can also be of type boolean and double and integer and null and string; however, parameter $array of array_keys() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

260
            'projects' => array_keys(/** @scrutinizer ignore-type */ $this->container->getParameter('assessments')),
Loading history...
261
            'config' => $this->container->getParameter('assessments'),
262
        ]);
263
264
        return $response;
265
    }
266
267
    /**
268
     * Transform given wikitext to HTML using the XTools parser. Wikitext must be passed in as the query 'wikitext'.
269
     * @Route("/api/project/parser/{project}")
270
     * @return JsonResponse Safe HTML.
271
     */
272
    public function wikifyApiAction(): JsonResponse
273
    {
274
        return new JsonResponse(
275
            Edit::wikifyString($this->request->query->get('wikitext', ''), $this->project)
276
        );
277
    }
278
}
279