DelegatingTicketStore   A
last analyzed

Complexity

Total Complexity 23

Size/Duplication

Total Lines 132
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 23
eloc 64
dl 0
loc 132
rs 10
c 0
b 0
f 0

4 Methods

Rating   Name   Duplication   Size   Complexity  
B __construct() 0 30 6
A addTicket() 0 20 6
B getTicket() 0 25 7
A deleteTicket() 0 12 4
1
<?php
2
3
declare(strict_types=1);
4
5
namespace SimpleSAML\Module\casserver\Cas\Ticket;
6
7
use Exception;
8
use InvalidArgumentException;
9
use SimpleSAML\Assert\Assert;
10
use SimpleSAML\Configuration;
11
use SimpleSAML\Logger;
12
use SimpleSAML\Module;
13
14
/**
15
 * Ticket store that delegates to other ticket stores.
16
 * Allows you to use multiple source for redundancy or allows a transition from one source to another.
17
 */
18
class DelegatingTicketStore extends TicketStore
19
{
20
    /**
21
     * @var string Delegate to 'all', 'first', or a named entry.
22
     */
23
    private string $delegateTo = 'all';
24
25
    /**
26
     * @var \SimpleSAML\Module\casserver\Cas\Ticket\TicketStore[]
27
     */
28
    private array $ticketStores = [];
29
30
    /**
31
     * @var \SimpleSAML\Module\casserver\Cas\Ticket\TicketStore
32
     */
33
    private TicketStore $primaryDelegate;
34
35
36
    /**
37
     * @param \SimpleSAML\Configuration $casConfig The cas configuration.
38
     */
39
    public function __construct(Configuration $casConfig)
40
    {
41
        $config = $casConfig->getConfigItem('ticketstore');
42
        $this->delegateTo = $config->getOptionalString('delegateTo', 'all');
43
        foreach ($config->getArray('ticketStores') as $name => $storeArray) {
44
            // TicketStore expects the store config to be in a specific item
45
            $storeConfig = Configuration::loadFromArray(['ticketstore' => $storeArray]);
46
            $class = $storeConfig->getConfigItem('ticketstore')->getString('class');
47
            $ticketStoreClass = Module::resolveClass($class, 'Cas\Ticket');
48
            try {
49
                /**
50
                 * @var TicketStore $ticketStore
51
                 * @psalm-suppress InvalidStringClass
52
                 */
53
                $ticketStore = new $ticketStoreClass($storeConfig);
54
                $this->ticketStores[$name] = $ticketStore;
55
            } catch (Exception $e) {
56
                Logger::error("Unable to create ticket store '$name'. Error " . $e->getMessage());
57
            }
58
        }
59
        Assert::notEmpty($this->ticketStores, "'ticketStores' must be defined.");
60
        $this->ticketStores = $this->ticketStores;
61
62
        if ($this->delegateTo === 'first') {
63
            $this->primaryDelegate = reset($this->ticketStores);
64
        } elseif ($this->delegateTo !== 'all') {
65
            if (array_key_exists($this->delegateTo, $this->ticketStores)) {
66
                $this->primaryDelegate = $this->ticketStores[$this->delegateTo];
67
            } else {
68
                throw new InvalidArgumentException("No ticket store called '" . $this->delegateTo . "'");
69
            }
70
        }
71
    }
72
73
    /**
74
     * Get the ticket, searching one or all of the delegates
75
     * @param string $ticketId The ticket to find
76
     * @return array|null The ticket or null if none found
77
     * @throws \Exception from any delegate stores ONLY if no delegates worked
78
     */
79
    public function getTicket(string $ticketId): ?array
80
    {
81
        if ($this->delegateTo === 'all') {
82
            $ticket = null;
83
            $rethrowException = null;
84
            foreach ($this->ticketStores as $name => $store) {
85
                try {
86
                    $ticket = $store->getTicket($ticketId);
87
                    $rethrowException = false; // no need to rethrow, at least one store worked
88
                } catch (Exception $e) {
89
                    if ($rethrowException === null) {
90
                        $rethrowException = $e;
91
                    }
92
                    Logger::error("Unable to read tickets from '$name'. Trying next store. Error " . $e->getMessage());
93
                }
94
                if ($ticket) {
95
                    return $ticket;
96
                }
97
            }
98
            if ($rethrowException) {
99
                throw $rethrowException;
100
            }
101
            return $ticket;
102
        } else {
103
            return $this->primaryDelegate->getTicket($ticketId);
104
        }
105
    }
106
107
    /**
108
     * @param array $ticket Ticket to add
109
     * @throws \Exception from any delegate stores ONLY if no delegates worked
110
     */
111
    public function addTicket(array $ticket): void
112
    {
113
        if ($this->delegateTo === 'all') {
114
            $rethrowException = null;
115
            foreach ($this->ticketStores as $name => $store) {
116
                try {
117
                    $store->addTicket($ticket);
118
                    $rethrowException = false; // no need to rethrow, at least one store worked
119
                } catch (Exception $e) {
120
                    if ($rethrowException === null) {
121
                        $rethrowException = $e;
122
                    }
123
                    Logger::error("Unable to add ticket to '$name'. Continue to next store. Error" . $e->getMessage());
124
                }
125
            }
126
            if ($rethrowException) {
127
                throw $rethrowException;
128
            }
129
        } else {
130
            $this->primaryDelegate->addTicket($ticket);
131
        }
132
    }
133
134
135
    /**
136
     * @param string $ticketId Ticket to delete
137
     */
138
    public function deleteTicket(string $ticketId): void
139
    {
140
        if ($this->delegateTo === 'all') {
141
            foreach ($this->ticketStores as $name => $store) {
142
                try {
143
                    $store->deleteTicket($ticketId);
144
                } catch (Exception $e) {
145
                    Logger::error("Unable to delete ticket from '$name'. Trying next store. Error" . $e->getMessage());
146
                }
147
            }
148
        } else {
149
            $this->primaryDelegate->deleteTicket($ticketId);
150
        }
151
    }
152
}
153