Passed
Push — master ( a343df...d20b40 )
by Tim
10:40
created

AbstractAlgorithmFactory::__construct()   A

Complexity

Conditions 6
Paths 5

Size

Total Lines 20
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 7
c 1
b 0
f 0
nc 5
nop 2
dl 0
loc 20
rs 9.2222
1
<?php
2
3
declare(strict_types=1);
4
5
namespace SimpleSAML\XMLSecurity\Alg;
6
7
use SimpleSAML\Assert\Assert;
8
use SimpleSAML\XMLSecurity\Exception\BlacklistedAlgorithmException;
9
use SimpleSAML\XMLSecurity\Exception\UnsupportedAlgorithmException;
10
use SimpleSAML\XMLSecurity\Key\KeyInterface;
11
12
/**
13
 * An abstract class implementing an algorithm factory.
14
 *
15
 * Extending classes MUST declare two static properties:
16
 *
17
 * - $cache: an associative array holding algorithm IDs as the keys, and class names implementing those algorithms as
18
 *   the value.
19
 * - $initialized: a boolean telling whether this factory has been initialized (a constructor has been called) or not.
20
 *
21
 * @package simplesamlphp/xml-security
22
 */
23
abstract class AbstractAlgorithmFactory
24
{
25
    /**
26
     * A cache of algorithm implementations indexed by algorithm ID.
27
     *
28
     * @var string[]
29
     */
30
    protected static array $cache = [];
31
32
    /**
33
     * Whether the factory has been initialized or not.
34
     *
35
     * @var bool
36
     */
37
    protected static bool $initialized = false;
38
39
40
    /**
41
     * Build a factory that creates algorithms.
42
     *
43
     * @param string[]|null $blacklist A list of algorithms forbidden for their use.
44
     * @param string[]|null $defaults A list of known implementations.
45
     */
46
    public function __construct(
47
        protected ?array $blacklist = null,
48
        ?array $defaults = null,
49
    ) {
50
        // initialize the cache for supported algorithms per known implementation
51
        if (!static::$initialized && $defaults !== null) {
52
            foreach ($defaults as $algorithm) {
53
                /** @var \SimpleSAML\XMLSecurity\Alg\AlgorithmInterface $algorithm */
54
                foreach ($algorithm::getSupportedAlgorithms() as $algId) {
55
                    if (array_key_exists($algId, static::$cache)) {
56
                        /*
57
                         * If the key existed before initialization, that means someone registered a handler for this
58
                         * algorithm, so we should respect that and skip registering the default here.
59
                         */
60
                        continue;
61
                    }
62
                    static::$cache[$algId] = $algorithm;
63
                }
64
            }
65
            static::$initialized = true;
66
        }
67
    }
68
69
70
    /**
71
     * Get a new object implementing the given algorithm.
72
     *
73
     * @param string $algId The identifier of the algorithm desired.
74
     * @param \SimpleSAML\XMLSecurity\Key\KeyInterface $key The key to use with the given algorithm.
75
     *
76
     * @return \SimpleSAML\XMLSecurity\Alg\AlgorithmInterface An object implementing the given algorithm.
77
     *
78
     * @throws \SimpleSAML\XMLSecurity\Exception\InvalidArgumentException If an error occurs, e.g. the given algorithm
79
     * is blacklisted, unknown or the given key is not suitable for it.
80
     */
81
    public function getAlgorithm(string $algId, KeyInterface $key): AlgorithmInterface
82
    {
83
        Assert::false(
84
            in_array($algId, $this->blacklist, true),
0 ignored issues
show
Bug introduced by
It seems like $this->blacklist can also be of type null; however, parameter $haystack of in_array() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

84
            in_array($algId, /** @scrutinizer ignore-type */ $this->blacklist, true),
Loading history...
85
            sprintf('Blacklisted algorithm: \'%s\'.', $algId),
86
            BlacklistedAlgorithmException::class,
87
        );
88
        Assert::keyExists(
89
            static::$cache,
90
            $algId,
91
            sprintf('Unknown or unsupported algorithm: \'%s\'.', $algId),
92
            UnsupportedAlgorithmException::class,
93
        );
94
95
        return new static::$cache[$algId]($key, $algId);
96
    }
97
98
99
    /**
100
     * Get the name of the abstract class our algorithm implementations must extend.
101
     *
102
     * @return string
103
     */
104
    abstract protected static function getExpectedParent(): string;
105
106
107
    /**
108
     * Register an implementation of some algorithm(s) for its use.
109
     *
110
     * @param string $className
111
     */
112
    public static function registerAlgorithm(string $className): void
113
    {
114
        $parent = static::getExpectedParent();
115
        Assert::subclassOf(
116
            $className,
117
            $parent,
118
            'Cannot register algorithm "' . $className . '", must implement ' . $parent . '.',
119
        );
120
121
        /** @var \SimpleSAML\XMLSecurity\Alg\AlgorithmInterface $className */
122
        foreach ($className::getSupportedAlgorithms() as $algId) {
123
            static::$cache[$algId] = $className;
124
        }
125
    }
126
}
127