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

Cas20Controller::proxy()   B

Complexity

Conditions 6
Paths 4

Size

Total Lines 74
Code Lines 41

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 41
c 0
b 0
f 0
dl 0
loc 74
rs 8.6417
cc 6
nc 4
nop 3

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace SimpleSAML\Module\casserver\Controller;
6
7
use SimpleSAML\CAS\Constants as C;
8
use SimpleSAML\Configuration;
9
use SimpleSAML\Logger;
10
use SimpleSAML\Module\casserver\Cas\Factories\TicketFactory;
11
use SimpleSAML\Module\casserver\Cas\Protocol\Cas20;
12
use SimpleSAML\Module\casserver\Controller\Traits\UrlTrait;
13
use SimpleSAML\Module\casserver\Http\XmlResponse;
14
use SimpleSAML\Utils;
15
use Symfony\Component\HttpFoundation\Request;
16
use Symfony\Component\HttpFoundation\Response;
17
use Symfony\Component\HttpKernel\Attribute\AsController;
18
use Symfony\Component\HttpKernel\Attribute\MapQueryParameter;
19
20
#[AsController]
21
class Cas20Controller
22
{
23
    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...troller\Cas20Controller: $request, $query
Loading history...
24
25
    /** @var Logger */
26
    protected Logger $logger;
27
28
    /** @var Utils\HTTP */
29
    protected Utils\HTTP $httpUtils;
30
31
    /** @var Configuration */
32
    protected Configuration $casConfig;
33
34
    /** @var Cas20 */
35
    protected Cas20 $cas20Protocol;
36
37
    /** @var TicketFactory */
38
    protected TicketFactory $ticketFactory;
39
40
    // this could be any configured ticket store
41
    protected mixed $ticketStore;
42
43
    /**
44
     * @param   Configuration       $sspConfig
45
     * @param   Configuration|null  $casConfig
46
     * @param                       $ticketStore
47
     *
48
     * @throws \Exception
49
     */
50
    public function __construct(
51
        private readonly Configuration $sspConfig,
52
        Configuration $casConfig = null,
53
        $ticketStore = null,
54
        Utils\HTTP $httpUtils = null,
55
    ) {
56
        // We are using this work around in order to bypass Symfony's autowiring for cas configuration. Since
57
        // the configuration class is the same, it loads the ssp configuration twice. Still, we need the constructor
58
        // argument in order to facilitate testing
59
        $this->casConfig = ($casConfig === null || $casConfig === $sspConfig)
60
            ? Configuration::getConfig('module_casserver.php') : $casConfig;
61
        $this->cas20Protocol = new Cas20($this->casConfig);
62
        /* Instantiate ticket factory */
63
        $this->ticketFactory = new TicketFactory($this->casConfig);
64
        /* Instantiate ticket store */
65
        $ticketStoreConfig = $this->casConfig->getOptionalValue(
66
            'ticketstore',
67
            ['class' => 'casserver:FileSystemTicketStore'],
68
        );
69
        $ticketStoreClass  = 'SimpleSAML\\Module\\casserver\\Cas\\Ticket\\'
70
            . explode(':', $ticketStoreConfig['class'])[1];
71
        $this->ticketStore = $ticketStore ?? new $ticketStoreClass($this->casConfig);
72
        $this->httpUtils = $httpUtils ?? new Utils\HTTP();
73
    }
74
75
    /**
76
     * @param   Request      $request
77
     * @param   string       $TARGET
78
     * @param   bool  $renew  [OPTIONAL] - if this parameter is set, ticket validation will only succeed
79
     *                        if the service ticket was issued from the presentation of the user’s primary
80
     *                        credentials. It will fail if the ticket was issued from a single sign-on session.
81
     * @param   string|null  $ticket  [REQUIRED] - the service ticket issued by /login
82
     * @param   string|null  $service [REQUIRED] - the identifier of the service for which the ticket was issued
83
     * @param   string|null  $pgtUrl  [OPTIONAL] - the URL of the proxy callback
84
     *
85
     * @return XmlResponse
86
     */
87
    public function serviceValidate(
88
        Request $request,
89
        #[MapQueryParameter] string $TARGET = null,
90
        #[MapQueryParameter] bool $renew = false,
91
        #[MapQueryParameter] ?string $ticket = null,
92
        #[MapQueryParameter] ?string $service = null,
93
        #[MapQueryParameter] ?string $pgtUrl = null,
94
    ): XmlResponse {
95
        return $this->validate(
96
            request: $request,
97
            method:  'serviceValidate',
98
            renew:   $renew,
99
            target:  $TARGET,
100
            ticket:  $ticket,
101
            service: $service,
102
            pgtUrl:  $pgtUrl,
103
        );
104
    }
105
106
    /**
107
     * /proxy provides proxy tickets to services that have
108
     * acquired proxy-granting tickets and will be proxying authentication to back-end services.
109
     *
110
     * @param   Request      $request
111
     * @param   string|null  $targetService [REQUIRED] - the service identifier of the back-end service.
112
     * @param   string|null  $pgt  [REQUIRED] - the proxy-granting ticket acquired by the service
113
     *                             during service ticket or proxy ticket validation.
114
     *
115
     * @return XmlResponse
116
     */
117
    public function proxy(
118
        Request $request,
119
        #[MapQueryParameter] ?string $targetService = null,
120
        #[MapQueryParameter] ?string $pgt = null,
121
    ): XmlResponse {
122
        $legal_target_service_urls = $this->casConfig->getOptionalValue('legal_target_service_urls', []);
123
        // Fail if
124
        $message = match (true) {
125
            // targetService pareameter is not defined
126
            $targetService === null => 'Missing target service parameter [targetService]',
127
            // pgt parameter is not defined
128
            $pgt === null => 'Missing proxy granting ticket parameter: [pgt]',
129
            !$this->checkServiceURL($this->sanitize($targetService), $legal_target_service_urls) =>
0 ignored issues
show
Deprecated Code introduced by
The function SimpleSAML\Module\casser...ller::checkServiceURL() has been deprecated. ( Ignorable by Annotation )

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

129
            !/** @scrutinizer ignore-deprecated */ $this->checkServiceURL($this->sanitize($targetService), $legal_target_service_urls) =>
Loading history...
Bug introduced by
It seems like $targetService can also be of type null; however, parameter $parameter of SimpleSAML\Module\casser...0Controller::sanitize() 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

129
            !$this->checkServiceURL($this->sanitize(/** @scrutinizer ignore-type */ $targetService), $legal_target_service_urls) =>
Loading history...
130
                "Target service parameter not listed as a legal service: [targetService] = {$targetService}",
131
            default => null,
132
        };
133
134
        if (!empty($message)) {
135
            return new XmlResponse(
136
                (string)$this->cas20Protocol->getValidateFailureResponse(C::ERR_INVALID_REQUEST, $message),
137
                Response::HTTP_BAD_REQUEST,
138
            );
139
        }
140
141
        // Get the ticket
142
        $proxyGrantingTicket = $this->ticketStore->getTicket($pgt);
143
        $message = match (true) {
144
            // targetService parameter is not defined
145
            $proxyGrantingTicket === null => "Ticket {$pgt} not recognized",
146
            // pgt parameter is not defined
147
            !$this->ticketFactory->isProxyGrantingTicket($proxyGrantingTicket)
148
            => "Not a valid proxy granting ticket id: {$pgt}",
149
            default => null,
150
        };
151
152
        if (!empty($message)) {
153
            return new XmlResponse(
154
                (string)$this->cas20Protocol->getValidateFailureResponse('BAD_PGT', $message),
155
                Response::HTTP_BAD_REQUEST,
156
            );
157
        }
158
159
        // Get the session id from the ticket
160
        $sessionTicket = $this->ticketStore->getTicket($proxyGrantingTicket['sessionId']);
161
162
        if (
163
            $sessionTicket === null
164
            || $this->ticketFactory->isSessionTicket($sessionTicket) === false
165
            || $this->ticketFactory->isExpired($sessionTicket)
166
        ) {
167
            $message = "Ticket {$pgt} has expired";
168
            Logger::debug('casserver:' . $message);
169
170
            return new XmlResponse(
171
                (string)$this->cas20Protocol->getValidateFailureResponse('BAD_PGT', $message),
172
                Response::HTTP_BAD_REQUEST,
173
            );
174
        }
175
176
        $proxyTicket = $this->ticketFactory->createProxyTicket(
177
            [
178
                'service' => $targetService,
179
                'forceAuthn' => $proxyGrantingTicket['forceAuthn'],
180
                'attributes' => $proxyGrantingTicket['attributes'],
181
                'proxies' => $proxyGrantingTicket['proxies'],
182
                'sessionId' => $proxyGrantingTicket['sessionId'],
183
                ],
184
        );
185
186
        $this->ticketStore->addTicket($proxyTicket);
187
188
        return new XmlResponse(
189
            (string)$this->cas20Protocol->getProxySuccessResponse($proxyTicket['id']),
190
            Response::HTTP_OK,
191
        );
192
    }
193
194
    /**
195
     * @param   Request      $request
196
     * @param   string       $TARGET  // todo: this should go away???
197
     * @param   bool  $renew  [OPTIONAL] - if this parameter is set, ticket validation will only succeed
198
     *                        if the service ticket was issued from the presentation of the user’s primary
199
     *                        credentials. It will fail if the ticket was issued from a single sign-on session.
200
     * @param   string|null  $ticket  [REQUIRED] - the service ticket issued by /login
201
     * @param   string|null  $service  [REQUIRED] - the identifier of the service for which the ticket was issued
202
     * @param   string|null  $pgtUrl  [OPTIONAL] - the URL of the proxy callback
203
     * @return XmlResponse
204
     */
205
    public function proxyValidate(
206
        Request $request,
207
        #[MapQueryParameter] string $TARGET = null,
208
        #[MapQueryParameter] bool $renew = false,
209
        #[MapQueryParameter] ?string $ticket = null,
210
        #[MapQueryParameter] ?string $service = null,
211
        #[MapQueryParameter] ?string $pgtUrl = null,
212
    ): XmlResponse {
213
        return $this->validate(
214
            request: $request,
215
            method:  'proxyValidate',
216
            renew:   $renew,
217
            target:  $TARGET,
218
            ticket:  $ticket,
219
            service: $service,
220
            pgtUrl:  $pgtUrl,
221
        );
222
    }
223
224
    /**
225
     * Used by the unit tests
226
     *
227
     * @return mixed
228
     */
229
    public function getTicketStore(): mixed
230
    {
231
        return $this->ticketStore;
232
    }
233
}
234