Passed
Pull Request — master (#45)
by
unknown
22:45
created

LogoutController::logout()   B

Complexity

Conditions 8
Paths 48

Size

Total Lines 41
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 19
c 1
b 0
f 0
dl 0
loc 41
rs 8.4444
cc 8
nc 48
nop 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace SimpleSAML\Module\casserver\Controller;
6
7
use SimpleSAML\Auth\Simple;
8
use SimpleSAML\Compat\SspContainer;
9
use SimpleSAML\Configuration;
10
use SimpleSAML\Logger;
11
use SimpleSAML\Module;
12
use SimpleSAML\Module\casserver\Cas\Factories\TicketFactory;
13
use SimpleSAML\Module\casserver\Cas\Ticket\TicketStore;
14
use SimpleSAML\Module\casserver\Controller\Traits\UrlTrait;
15
use SimpleSAML\Session;
16
use Symfony\Component\HttpFoundation\RedirectResponse;
17
use Symfony\Component\HttpFoundation\Request;
18
use Symfony\Component\HttpKernel\Attribute\AsController;
19
use Symfony\Component\HttpKernel\Attribute\MapQueryParameter;
20
21
#[AsController]
22
class LogoutController
23
{
24
    use UrlTrait;
0 ignored issues
show
introduced by
The trait SimpleSAML\Module\casser...troller\Traits\UrlTrait requires some properties which are not provided by SimpleSAML\Module\casser...roller\LogoutController: $request, $httpUtils, $query, $cas20Protocol
Loading history...
25
26
    /** @var Logger */
27
    protected Logger $logger;
28
29
    /** @var Configuration */
30
    protected Configuration $casConfig;
31
32
    /** @var TicketFactory */
33
    protected TicketFactory $ticketFactory;
34
35
    /** @var Simple  */
36
    protected Simple $authSource;
37
38
    /** @var SspContainer */
39
    protected SspContainer $container;
40
41
    /** @var TicketStore */
42
    protected TicketStore $ticketStore;
43
44
45
    /**
46
     * @param   Configuration|null  $casConfig
47
     * @param   Simple|null         $source
48
     * @param   SspContainer|null   $container
49
     *
50
     * @throws \Exception
51
     */
52
    public function __construct(
53
        private readonly Configuration $sspConfig,
54
        // Facilitate testing
55
        Configuration $casConfig = null,
56
        Simple $source = null,
57
        SspContainer $container = null,
58
    ) {
59
        // We are using this work around in order to bypass Symfony's autowiring for cas configuration. Since
60
        // the configuration class is the same, it loads the ssp configuration twice. Still, we need the constructor
61
        // argument in order to facilitate testin.
62
        $this->casConfig = ($casConfig === null || $casConfig === $sspConfig)
63
            ? Configuration::getConfig('module_casserver.php') : $casConfig;
64
        $this->authSource = $source ?? new Simple($this->casConfig->getValue('authsource'));
65
        $this->container = $container ?? new SspContainer();
66
67
        /* Instantiate ticket factory */
68
        $this->ticketFactory = new TicketFactory($this->casConfig);
69
        /* Instantiate ticket store */
70
        $ticketStoreConfig = $this->casConfig->getOptionalValue(
71
            'ticketstore',
72
            ['class' => 'casserver:FileSystemTicketStore'],
73
        );
74
        $ticketStoreClass = 'SimpleSAML\\Module\\casserver\\Cas\\Ticket\\'
75
            . explode(':', $ticketStoreConfig['class'])[1];
76
        $this->ticketStore = new $ticketStoreClass($this->casConfig);
77
    }
78
79
    /**
80
     *
81
     * @param   Request      $request
82
     * @param   string|null  $url
83
     *
84
     * @return RedirectResponse|null
85
     */
86
    public function logout(
87
        Request $request,
88
        #[MapQueryParameter] ?string $url = null,
89
    ): RedirectResponse|null {
90
        if (!$this->casConfig->getOptionalValue('enable_logout', false)) {
91
            $this->handleExceptionThrown('Logout not allowed');
92
        }
93
94
        // Skip Logout Page configuration
95
        $skipLogoutPage = $this->casConfig->getOptionalValue('skip_logout_page', false);
96
97
        if ($skipLogoutPage && $url === null) {
98
            $this->handleExceptionThrown('Required URL query parameter [url] not provided. (CAS Server)');
99
        }
100
101
        // Construct the logout redirect url
102
        if ($skipLogoutPage) {
103
            $logoutRedirectUrl = $url;
104
            $params = [];
105
        } else {
106
            $logoutRedirectUrl = Module::getModuleURL('casserver/loggedOut');
107
            $params =  $url === null ? []
108
                : ['url' => $url];
109
        }
110
111
        // Delete the ticket from the session
112
        $session = $this->getSession();
113
        if ($session !== null) {
114
            $this->ticketStore->deleteTicket($session->getSessionId());
0 ignored issues
show
Bug introduced by
It seems like $session->getSessionId() can also be of type null; however, parameter $ticketId of SimpleSAML\Module\casser...etStore::deleteTicket() does only seem to accept string, 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

114
            $this->ticketStore->deleteTicket(/** @scrutinizer ignore-type */ $session->getSessionId());
Loading history...
115
        }
116
117
        // Redirect
118
        if (!$this->authSource->isAuthenticated()) {
119
            $this->container->redirect($logoutRedirectUrl, $params);
0 ignored issues
show
Bug introduced by
It seems like $logoutRedirectUrl can also be of type null; however, parameter $url of SimpleSAML\Compat\SspContainer::redirect() does only seem to accept string, 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

119
            $this->container->redirect(/** @scrutinizer ignore-type */ $logoutRedirectUrl, $params);
Loading history...
120
        }
121
122
        // Logout and redirect
123
        $this->authSource->logout($logoutRedirectUrl);
124
125
        // We should never get here
126
        return null;
127
    }
128
129
    /**
130
     * @return mixed
131
     */
132
    public function getTicketStore(): mixed
133
    {
134
        return $this->ticketStore;
135
    }
136
137
    /**
138
     * @param   string  $message
139
     *
140
     * @return void
141
     */
142
    protected function handleExceptionThrown(string $message): void
143
    {
144
        Logger::debug('casserver:' . $message);
145
        throw new \RuntimeException($message);
146
    }
147
148
    /**
149
     * Get the Session
150
     *
151
     * @return Session|null
152
     */
153
    protected function getSession(): ?Session
154
    {
155
        return Session::getSession();
156
    }
157
}
158