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

Cas10Controller::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 15
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

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

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