Passed
Push — master ( 81b6b5...5b35ab )
by Tim
03:44
created

LogoutController::logout()   B

Complexity

Conditions 8
Paths 48

Size

Total Lines 40
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 20
c 1
b 0
f 0
dl 0
loc 40
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\Configuration;
9
use SimpleSAML\HTTP\RunnableResponse;
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 SimpleSAML\Utils;
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;
1 ignored issue
show
introduced by
The trait SimpleSAML\Module\casser...troller\Traits\UrlTrait requires some properties which are not provided by SimpleSAML\Module\casser...roller\LogoutController: $query, $request
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 Utils\HTTP */
39
    protected Utils\HTTP $httpUtils;
40
41
    /** @var TicketStore */
42
    protected TicketStore $ticketStore;
43
44
45
    /**
46
     * @param   Configuration       $sspConfig
47
     * @param   Configuration|null  $casConfig
48
     * @param   Simple|null         $source
49
     * @param   Utils\HTTP|null     $httpUtils
50
     *
51
     * @throws \Exception
52
     */
53
    public function __construct(
54
        private readonly Configuration $sspConfig,
55
        // Facilitate testing
56
        Configuration $casConfig = null,
57
        Simple $source = null,
58
        Utils\HTTP $httpUtils = null,
59
    ) {
60
        // We are using this work around in order to bypass Symfony's autowiring for cas configuration. Since
61
        // the configuration class is the same, it loads the ssp configuration twice. Still, we need the constructor
62
        // argument in order to facilitate testin.
63
        $this->casConfig = ($casConfig === null || $casConfig === $sspConfig)
64
            ? Configuration::getConfig('module_casserver.php') : $casConfig;
65
        $this->authSource = $source ?? new Simple($this->casConfig->getValue('authsource'));
66
        $this->httpUtils = $httpUtils ?? new Utils\HTTP();
67
68
        /* Instantiate ticket factory */
69
        $this->ticketFactory = new TicketFactory($this->casConfig);
70
        /* Instantiate ticket store */
71
        $ticketStoreConfig = $this->casConfig->getOptionalValue(
72
            'ticketstore',
73
            ['class' => 'casserver:FileSystemTicketStore'],
74
        );
75
        $ticketStoreClass = Module::resolveClass($ticketStoreConfig['class'], 'Cas\Ticket');
76
        $this->ticketStore = new $ticketStoreClass($this->casConfig);
77
    }
78
79
    /**
80
     *
81
     * @param   Request      $request
82
     * @param   string|null  $url
83
     *
84
     * @return RunnableResponse
85
     */
86
    public function logout(
87
        Request $request,
88
        #[MapQueryParameter] ?string $url = null,
89
    ): RunnableResponse {
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
            return new RunnableResponse([$this->httpUtils, 'redirectTrustedURL'], [$logoutRedirectUrl, $params]);
120
        }
121
122
        // Logout and redirect
123
        return new RunnableResponse(
124
            [$this->authSource, 'logout'],
125
            [$logoutRedirectUrl],
126
        );
127
    }
128
129
    /**
130
     * @return TicketStore
131
     */
132
    public function getTicketStore(): TicketStore
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