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

Cas10Controller   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 132
Duplicated Lines 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 15
eloc 70
c 2
b 0
f 0
dl 0
loc 132
rs 10

2 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 15 1
C validate() 0 84 14
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
16
#[AsController]
17
class Cas10Controller
18
{
19
    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...
20
21
    /** @var Logger */
22
    protected Logger $logger;
23
24
    /** @var Configuration */
25
    protected Configuration $casConfig;
26
27
    /** @var Cas10 */
28
    protected Cas10 $cas10Protocol;
29
30
    /** @var TicketFactory */
31
    protected TicketFactory $ticketFactory;
32
33
    // this could be any configured ticket store
34
    protected mixed $ticketStore;
35
36
    /**
37
     * Controller constructor.
38
     *
39
     * It initializes the global configuration for the controllers implemented here.
40
     *
41
     */
42
    public function __construct()
43
    {
44
        $this->casConfig = Configuration::getConfig('module_casserver.php');
45
        $this->cas10Protocol = new Cas10($this->casConfig);
46
        /* Instantiate ticket factory */
47
        $this->ticketFactory = new TicketFactory($this->casConfig);
48
        /* Instantiate ticket store */
49
        $ticketStoreConfig = $this->casConfig->getOptionalValue(
50
            'ticketstore',
51
            ['class' => 'casserver:FileSystemTicketStore'],
52
        );
53
        $ticketStoreClass = 'SimpleSAML\\Module\\casserver\\Cas\\Ticket\\'
54
            . explode(':', $ticketStoreConfig['class'])[1];
55
        /** @psalm-suppress InvalidStringClass */
56
        $this->ticketStore = new $ticketStoreClass($this->casConfig);
57
    }
58
59
    /**
60
     * @param   Request  $request
61
     *
62
     * @return Response
63
     */
64
    public function validate(Request $request): Response
65
    {
66
        // Check if any of the required query parameters are missing
67
        if (!$request->query->has('service')) {
68
            Logger::debug('casserver: Missing service parameter: [service]');
69
            return new Response(
70
                $this->cas10Protocol->getValidateFailureResponse(),
71
                Response::HTTP_BAD_REQUEST,
72
            );
73
        } elseif (!$request->query->has('ticket')) {
74
            Logger::debug('casserver: Missing service parameter: [ticket]');
75
            return new Response(
76
                $this->cas10Protocol->getValidateFailureResponse(),
77
                Response::HTTP_BAD_REQUEST,
78
            );
79
        }
80
81
        // Check if we are required to force an authentication
82
        $forceAuthn = $request->query->has('renew') && $request->query->get('renew');
83
        // Get the ticket
84
        $ticket = $request->query->get('ticket');
85
        // Get the service
86
        $service = $request->query->get('service');
87
88
        try {
89
            // Get the service ticket
90
            $serviceTicket = $this->ticketStore->getTicket($ticket);
91
            // Delete the ticket
92
            $this->ticketStore->deleteTicket($ticket);
93
        } catch (\Exception $e) {
94
            Logger::error('casserver:validate: internal server error. ' . var_export($e->getMessage(), true));
95
            return new Response(
96
                $this->cas10Protocol->getValidateFailureResponse(),
97
                Response::HTTP_INTERNAL_SERVER_ERROR,
98
            );
99
        }
100
101
        $failed = false;
102
        $message = '';
103
        // No ticket
104
        if ($serviceTicket === null) {
105
            $message = 'ticket: ' . var_export($ticket, true) . ' not recognized';
106
            $failed = true;
107
            // This is not a service ticket
108
        } elseif (!$this->ticketFactory->isServiceTicket($serviceTicket)) {
109
            $message = 'ticket: ' . var_export($ticket, true) . ' is not a service ticket';
110
            $failed = true;
111
            // the ticket has expired
112
        } elseif ($this->ticketFactory->isExpired($serviceTicket)) {
113
            $message = 'Ticket has ' . var_export($ticket, true) . ' expired';
114
            $failed = true;
115
        } elseif ($this->sanitize($serviceTicket['service']) === $this->sanitize($service)) {
116
            $message = 'Mismatching service parameters: expected ' .
117
                var_export($serviceTicket['service'], true) .
118
                ' but was: ' . var_export($service, true);
119
            $failed = true;
120
        } elseif ($forceAuthn && isset($serviceTicket['forceAuthn']) && $serviceTicket['forceAuthn']) {
121
            $message = 'Ticket was issued from single sign on session';
122
            $failed = true;
123
        }
124
125
        if ($failed) {
126
            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

126
            Logger::/** @scrutinizer ignore-call */ 
127
                    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...
127
            return new Response(
128
                $this->cas10Protocol->getValidateFailureResponse(),
129
                Response::HTTP_BAD_REQUEST,
130
            );
131
        }
132
133
        // Get the username field
134
        $usernameField = $this->casConfig->getOptionalValue('attrname', 'eduPersonPrincipalName');
135
136
        // Fail if the username field is not present in the attribute list
137
        if (!\array_key_exists($usernameField, $serviceTicket['attributes'])) {
138
            Logger::error(
139
                'casserver:validate: internal server error. Missing user name attribute: '
140
                . var_export($usernameField, true),
141
            );
142
        }
143
144
        // Successful validation
145
        return new Response(
146
            $this->cas10Protocol->getValidateSuccessResponse($serviceTicket['attributes'][$usernameField][0]),
147
            Response::HTTP_OK,
148
        );
149
    }
150
}
151