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

Cas10Controller::validate()   C

Complexity

Conditions 13
Paths 22

Size

Total Lines 86
Code Lines 50

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
eloc 50
c 3
b 0
f 0
dl 0
loc 86
rs 6.6166
cc 13
nc 22
nop 4

How to fix   Long Method    Complexity   

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\Configuration;
8
use SimpleSAML\Logger;
9
use SimpleSAML\Module\casserver\Cas\Factories\TicketFactory;
10
use SimpleSAML\Module\casserver\Cas\Protocol\Cas10;
11
use SimpleSAML\Module\casserver\Controller\Traits\UrlTrait;
12
use Symfony\Component\HttpFoundation\Request;
13
use Symfony\Component\HttpFoundation\Response;
14
use Symfony\Component\HttpKernel\Attribute\AsController;
15
use Symfony\Component\HttpKernel\Attribute\MapQueryParameter;
16
17
#[AsController]
18
class Cas10Controller
19
{
20
    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...troller\Cas10Controller: $query, $request
Loading history...
21
22
    /** @var Logger */
23
    protected Logger $logger;
24
25
    /** @var Configuration */
26
    protected Configuration $casConfig;
27
28
    /** @var Cas10 */
29
    protected Cas10 $cas10Protocol;
30
31
    /** @var TicketFactory */
32
    protected TicketFactory $ticketFactory;
33
34
    // this could be any configured ticket store
35
    protected mixed $ticketStore;
36
37
    /**
38
     * @param   Configuration|null  $casConfig
39
     * @param                       $ticketStore
40
     *
41
     * @throws \Exception
42
     */
43
    public function __construct(
44
        Configuration $casConfig = null,
45
        $ticketStore = null,
46
    ) {
47
        $this->casConfig = $casConfig ?? Configuration::getConfig('module_casserver.php');
48
        $this->cas10Protocol = new Cas10($this->casConfig);
49
        /* Instantiate ticket factory */
50
        $this->ticketFactory = new TicketFactory($this->casConfig);
51
        /* Instantiate ticket store */
52
        $ticketStoreConfig = $this->casConfig->getOptionalValue(
53
            'ticketstore',
54
            ['class' => 'casserver:FileSystemTicketStore'],
55
        );
56
        $ticketStoreClass = 'SimpleSAML\\Module\\casserver\\Cas\\Ticket\\'
57
            . explode(':', $ticketStoreConfig['class'])[1];
58
        /** @psalm-suppress InvalidStringClass */
59
        $this->ticketStore = $ticketStore ?? new $ticketStoreClass($this->casConfig);
60
    }
61
62
    /**
63
     * @param   Request      $request
64
     * @param   bool         $renew
65
     * @param   string|null  $ticket
66
     * @param   string|null  $service
67
     *
68
     * @return Response
69
     */
70
    public function validate(
71
        Request $request,
72
        #[MapQueryParameter] bool $renew = false,
73
        #[MapQueryParameter] ?string $ticket = null,
74
        #[MapQueryParameter] ?string $service = null,
75
    ): Response {
76
77
        $forceAuthn = $renew;
78
        // Check if any of the required query parameters are missing
79
        if ($service === null || $ticket === null) {
80
            $messagePostfix = $service === null ? 'service' : 'ticket';
81
            Logger::debug("casserver: Missing service parameter: [{$messagePostfix}]");
82
            return new Response(
83
                $this->cas10Protocol->getValidateFailureResponse(),
84
                Response::HTTP_BAD_REQUEST,
85
            );
86
        }
87
88
        try {
89
            // Get the service ticket
90
            // `getTicket` uses the unserializable method and Objects may throw Throwables in their
91
            // unserialization handlers.
92
            $serviceTicket = $this->ticketStore->getTicket($ticket);
93
            // Delete the ticket
94
            $this->ticketStore->deleteTicket($ticket);
95
        } catch (\Exception $e) {
96
            Logger::error('casserver:validate: internal server error. ' . var_export($e->getMessage(), true));
97
            return new Response(
98
                $this->cas10Protocol->getValidateFailureResponse(),
99
                Response::HTTP_INTERNAL_SERVER_ERROR,
100
            );
101
        }
102
103
        $failed = false;
104
        $message = '';
105
        if (empty($serviceTicket)) {
106
            // No ticket
107
            $message = 'ticket: ' . var_export($ticket, true) . ' not recognized';
108
            $failed = true;
109
        } elseif (!$this->ticketFactory->isServiceTicket($serviceTicket)) {
110
            // This is not a service ticket
111
            $message = 'ticket: ' . var_export($ticket, true) . ' is not a service ticket';
112
            $failed = true;
113
        } elseif ($this->ticketFactory->isExpired($serviceTicket)) {
114
            // the ticket has expired
115
            $message = 'Ticket has ' . var_export($ticket, true) . ' expired';
116
            $failed = true;
117
        } elseif ($this->sanitize($serviceTicket['service']) !== $this->sanitize($service)) {
118
            // The service url we passed to the query parameters does not match the one in the ticket.
119
            $message = 'Mismatching service parameters: expected ' .
120
                var_export($serviceTicket['service'], true) .
121
                ' but was: ' . var_export($service, true);
122
            $failed = true;
123
        } elseif ($forceAuthn && !$serviceTicket['forceAuthn']) {
124
            // If `forceAuthn` is required but not set in the ticket
125
            $message = 'Ticket was issued from single sign on session';
126
            $failed = true;
127
        }
128
129
        if ($failed) {
130
            Logger::error('casserver:validate: ' . $message, true);
0 ignored issues
show
Unused Code introduced by
The call to SimpleSAML\Logger::error() has too many arguments starting with true. ( Ignorable by Annotation )

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

130
            Logger::/** @scrutinizer ignore-call */ 
131
                    error('casserver:validate: ' . $message, true);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
131
            return new Response(
132
                $this->cas10Protocol->getValidateFailureResponse(),
133
                Response::HTTP_BAD_REQUEST,
134
            );
135
        }
136
137
        // Get the username field
138
        $usernameField = $this->casConfig->getOptionalValue('attrname', 'eduPersonPrincipalName');
139
140
        // Fail if the username field is not present in the attribute list
141
        if (!\array_key_exists($usernameField, $serviceTicket['attributes'])) {
142
            Logger::error(
143
                'casserver:validate: internal server error. Missing user name attribute: '
144
                . var_export($usernameField, true),
145
            );
146
            return new Response(
147
                $this->cas10Protocol->getValidateFailureResponse(),
148
                Response::HTTP_BAD_REQUEST,
149
            );
150
        }
151
152
        // Successful validation
153
        return new Response(
154
            $this->cas10Protocol->getValidateSuccessResponse($serviceTicket['attributes'][$usernameField][0]),
155
            Response::HTTP_OK,
156
        );
157
    }
158
159
    /**
160
     * Used by the unit tests
161
     *
162
     * @return mixed
163
     */
164
    public function getTicketStore(): mixed
165
    {
166
        return $this->ticketStore;
167
    }
168
}
169