TicketValidator::validateAndDeleteTicket()   B
last analyzed

Complexity

Conditions 6
Paths 6

Size

Total Lines 36
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 21
c 1
b 0
f 0
nc 6
nop 2
dl 0
loc 36
rs 8.9617
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
        /** @var \SimpleSAML\Module\casserver\Cas\Ticket\TicketStore $this->ticketStore */
42
        $this->ticketStore = new $ticketStoreClass($casconfig);
43
        $ticketFactoryClass = Module::resolveClass('casserver:TicketFactory', 'Cas\Factories');
44
45
        /** @var \SimpleSAML\Module\casserver\Cas\Factories\TicketFactory $this->ticketStore */
46
        $this->ticketFactory = new $ticketFactoryClass($casconfig);
47
    }
48
49
50
    /**
51
     * @param string $ticket the ticket id to load validate
52
     * @param string $service the service that the ticket was issued to
53
     * @return string|array|null
54
     * @throws \SimpleSAML\Module\casserver\Cas\CasException Thrown if ticket doesn't exist, expired, service mismatch
55
     * @throws \InvalidArgumentException thrown if $ticket or $service parameter is missing
56
     */
57
    public function validateAndDeleteTicket(string $ticket, string $service)
58
    {
59
        if (empty($ticket)) {
60
            throw new InvalidArgumentException('Missing ticket parameter: [ticket]');
61
        }
62
        if (empty($service)) {
63
            throw new InvalidArgumentException('Missing service parameter: [service]');
64
        }
65
66
        $serviceTicket = $this->ticketStore->getTicket($ticket);
67
        if ($serviceTicket == null) {
68
            $message = 'Ticket ' . var_export($ticket, true) . ' not recognized';
69
            Logger::debug('casserver:' . $message);
70
            throw new CasException(C::ERR_INVALID_TICKET, $message);
71
        }
72
73
        // TODO: do proxy vs non proxy ticket check
74
        $this->ticketStore->deleteTicket($ticket);
75
76
        if ($this->ticketFactory->isExpired($serviceTicket)) {
77
            $message = 'Ticket ' . var_export($ticket, true) . ' has expired';
78
            Logger::debug('casserver:' . $message);
79
            throw new CasException(C::ERR_INVALID_TICKET, $message);
80
        }
81
82
        if (self::sanitize($serviceTicket['service']) !== self::sanitize($service)) {
83
            $message = 'Mismatching service parameters: expected ' .
84
                var_export($serviceTicket['service'], true) .
85
                ' but was: ' . var_export($service, true);
86
87
            Logger::debug('casserver:' . $message);
88
            throw new CasException(C::ERR_INVALID_SERVICE, $message);
89
        }
90
91
92
        return $serviceTicket;
93
    }
94
95
96
    /**
97
     * Java CAS clients are inconsistent with their sending of jsessionid, so remove it to
98
     * avoid service url matching issues.
99
     * @param string $parameter The service url to sanitize
100
     * @return string The sanitized url
101
     */
102
    public static function sanitize(string $parameter): string
103
    {
104
        return preg_replace(
105
            '/;jsessionid=.*[^?].*$/U',
106
            '',
107
            preg_replace('/;jsessionid=.*[?]/U', '?', urldecode($parameter)),
108
        );
109
    }
110
}
111