TicketValidator   A
last analyzed

Complexity

Total Complexity 8

Size/Duplication

Total Lines 96
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 8
eloc 37
dl 0
loc 96
rs 10
c 1
b 0
f 0

3 Methods

Rating   Name   Duplication   Size   Complexity  
A sanitize() 0 6 1
A __construct() 0 19 1
B validateAndDeleteTicket() 0 36 6
1
<?php
2
3
declare(strict_types=1);
4
5
namespace SimpleSAML\Module\casserver\Cas;
6
7
use InvalidArgumentException;
8
use SimpleSAML\CAS\Constants as C;
9
use SimpleSAML\Configuration;
10
use SimpleSAML\Logger;
11
use SimpleSAML\Module;
12
use SimpleSAML\Module\casserver\Cas\CasException;
13
use SimpleSAML\Module\casserver\Cas\Factories\TicketFactory;
14
use SimpleSAML\Module\casserver\Cas\Ticket\TicketStore;
15
16
class TicketValidator
17
{
18
    /** @var \SimpleSAML\Configuration */
19
    private Configuration $casconfig;
20
21
    /** @var \SimpleSAML\Module\casserver\Cas\Ticket\TicketStore */
22
    private TicketStore $ticketStore;
23
24
    /** @var \SimpleSAML\Module\casserver\Cas\Factories\TicketFactory */
25
    private TicketFactory $ticketFactory;
26
27
28
    /**
29
     * TicketValidator constructor.
30
     * @param \SimpleSAML\Configuration $casconfig
31
     */
32
    public function __construct(Configuration $casconfig)
33
    {
34
        $this->casconfig = $casconfig;
35
        $ticketStoreConfig = $casconfig->getOptionalValue(
36
            'ticketstore',
37
            ['class' => 'casserver:FileSystemTicketStore'],
38
        );
39
        $ticketStoreClass = Module::resolveClass($ticketStoreConfig['class'], 'Cas\Ticket');
40
        /**
41
         * @psalm-suppress InvalidStringClass
42
         * @var \SimpleSAML\Module\casserver\Cas\Ticket\TicketStore
43
         */
44
        $this->ticketStore = new $ticketStoreClass($casconfig);
45
        $ticketFactoryClass = Module::resolveClass('casserver:TicketFactory', 'Cas\Factories');
46
        /**
47
         * @psalm-suppress InvalidStringClass
48
         * @var \SimpleSAML\Module\casserver\Cas\Factories\TicketFactory
49
         */
50
        $this->ticketFactory = new $ticketFactoryClass($casconfig);
51
    }
52
53
54
    /**
55
     * @param string $ticket the ticket id to load validate
56
     * @param string $service the service that the ticket was issued to
57
     * @return string|array|null
58
     * @throws \SimpleSAML\Module\casserver\Cas\CasException Thrown if ticket doesn't exist, expired, service mismatch
59
     * @throws \InvalidArgumentException thrown if $ticket or $service parameter is missing
60
     */
61
    public function validateAndDeleteTicket(string $ticket, string $service)
62
    {
63
        if (empty($ticket)) {
64
            throw new InvalidArgumentException('Missing ticket parameter: [ticket]');
65
        }
66
        if (empty($service)) {
67
            throw new InvalidArgumentException('Missing service parameter: [service]');
68
        }
69
70
        $serviceTicket = $this->ticketStore->getTicket($ticket);
71
        if ($serviceTicket == null) {
72
            $message = 'Ticket ' . var_export($ticket, true) . ' not recognized';
73
            Logger::debug('casserver:' . $message);
74
            throw new CasException(C::ERR_INVALID_TICKET, $message);
75
        }
76
77
        // TODO: do proxy vs non proxy ticket check
78
        $this->ticketStore->deleteTicket($ticket);
79
80
        if ($this->ticketFactory->isExpired($serviceTicket)) {
81
            $message = 'Ticket ' . var_export($ticket, true) . ' has expired';
82
            Logger::debug('casserver:' . $message);
83
            throw new CasException(C::ERR_INVALID_TICKET, $message);
84
        }
85
86
        if (self::sanitize($serviceTicket['service']) !== self::sanitize($service)) {
87
            $message = 'Mismatching service parameters: expected ' .
88
                var_export($serviceTicket['service'], true) .
89
                ' but was: ' . var_export($service, true);
90
91
            Logger::debug('casserver:' . $message);
92
            throw new CasException(C::ERR_INVALID_SERVICE, $message);
93
        }
94
95
96
        return $serviceTicket;
97
    }
98
99
100
    /**
101
     * Java CAS clients are inconsistent with their sending of jsessionid, so remove it to
102
     * avoid service url matching issues.
103
     * @param string $parameter The service url to sanitize
104
     * @return string The sanitized url
105
     */
106
    public static function sanitize(string $parameter): string
107
    {
108
        return preg_replace(
109
            '/;jsessionid=.*[^?].*$/U',
110
            '',
111
            preg_replace('/;jsessionid=.*[?]/U', '?', urldecode($parameter)),
112
        );
113
    }
114
}
115