MetaDataStorageHandlerDirectory::getMetadataSet()   A
last analyzed

Complexity

Conditions 5
Paths 7

Size

Total Lines 26
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 14
nc 7
nop 1
dl 0
loc 26
rs 9.4888
c 1
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace SimpleSAML\Metadata;
6
7
use Exception;
8
use SimpleSAML\Assert\Assert;
9
use SimpleSAML\Configuration;
10
use SimpleSAML\Logger;
11
12
use function array_key_exists;
13
use function is_array;
14
15
/**
16
 * A directory that contains metadata files.
17
 * Instantiation of session handler objects should be done through
18
 * the class method getMetadataHandler().
19
 *
20
 * @package SimpleSAMLphp
21
 * This was created based on the MetaDataStorageHandlerFlatFile.php source in February 2024.
22
 */
23
24
class MetaDataStorageHandlerDirectory extends MetaDataStorageSource
25
{
26
    /**
27
     * This is the directory we will load metadata files from. The path will always end
28
     * with a '/'.
29
     *
30
     * @var string
31
     */
32
    private string $directory = '/';
33
34
35
    /**
36
     * This is an associative array which stores the different metadata sets we have loaded.
37
     *
38
     * @var array
39
     */
40
    private array $cachedMetadata = [];
41
42
43
    /**
44
     * This constructor initializes the flatfile metadata storage handler with the
45
     * specified configuration. The configuration is an associative array with the following
46
     * possible elements:
47
     * - 'directory': The directory we should load metadata from. The default directory is
48
     *                set in the 'metadatadir' configuration option in 'config.php'.
49
     *
50
     * @param array $config An associative array with the configuration for this handler.
51
     */
52
    protected function __construct(Configuration $globalConfig, array $config)
0 ignored issues
show
Unused Code introduced by
The parameter $globalConfig is not used and could be removed. ( Ignorable by Annotation )

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

52
    protected function __construct(/** @scrutinizer ignore-unused */ Configuration $globalConfig, array $config)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
53
    {
54
        parent::__construct();
55
56
        // get the configuration
57
        $globalConfig = Configuration::getInstance();
58
59
        // find the path to the directory we should search for metadata in
60
        if (array_key_exists('directory', $config)) {
61
            $this->directory = $config['directory'] ?: 'metadata/';
62
        } else {
63
            $this->directory = $globalConfig->getOptionalString('metadatadir', 'metadata/');
64
        }
65
66
        /* Resolve this directory relative to the SimpleSAMLphp directory (unless it is
67
         * an absolute path).
68
         */
69
70
        /** @var string $base */
71
        $base = $globalConfig->resolvePath($this->directory);
72
        $this->directory = $base . '/';
73
    }
74
75
76
    /**
77
     * This function loads the given set of metadata files in the metadata directory.
78
     * This function returns null if it is unable to locate the given set in the metadata directory.
79
     *
80
     * @param string $set The set of metadata we are loading.
81
     *
82
     * @return array|null An associative array with the metadata,
83
     *     or null if we are unable to load metadata from the given file.
84
     * @throws \Exception If the metadata set cannot be loaded.
85
     */
86
    private function load(string $set): ?array
87
    {
88
        $metadatasetdir = $this->directory . $set . '.d';
89
90
        if (!$this->fileSystem->exists($metadatasetdir)) {
91
            return null;
92
        }
93
94
        /** @psalm-var mixed $metadata   We cannot be sure what the include below will do with this var */
95
        $metadata = [];
96
97
        $dh = @opendir($metadatasetdir);
98
        if ($dh === false) {
99
            Logger::warning(
100
                'Directory metadata handler: Unable to open directory: ' . var_export($metadatasetdir, true),
101
            );
102
            return $metadata;
103
        }
104
105
        while (($entry = readdir($dh)) !== false) {
106
            if ($entry[0] === '.') {
107
                // skip '..', '.' and hidden files
108
                continue;
109
            }
110
111
            $path = $metadatasetdir . DIRECTORY_SEPARATOR . $entry;
112
            if (is_dir($path)) {
113
                Logger::warning(
114
                    'Directory metadata handler: Metadata directory contained a directory where only files should ' .
115
                    'exist: ' . var_export($path, true),
116
                );
117
                 continue;
118
            }
119
120
            if (str_ends_with($path, '.php') || str_ends_with($path, '.xml')) {
121
                $type = 'flatfile';
122
                if (str_ends_with($path, '.xml')) {
123
                    $type = 'xml';
124
                }
125
                Logger::info("loading set $set metadata file at $path");
126
127
                $config = [ 'type' => $type, 'file' => $path ];
128
                $source = MetaDataStorageSource::getSource($config);
129
                $md = $source->getMetadataSet($set);
130
                $metadata = array_merge($metadata, $md);
131
            }
132
        }
133
134
135
136
137
        if (!is_array($metadata)) {
0 ignored issues
show
introduced by
The condition is_array($metadata) is always true.
Loading history...
138
            throw new Exception('Could not load metadata set [' . $set . '] from file: ' . $metadatasetfile);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $metadatasetfile does not exist. Did you maybe mean $metadata?
Loading history...
139
        }
140
        return $metadata;
141
    }
142
143
144
    /**
145
     * This function retrieves the given set of metadata. It will return an empty array if it is
146
     * unable to locate it.
147
     *
148
     * @param string $set The set of metadata we are retrieving.
149
     *
150
     * @return array An associative array with the metadata. Each element in the array is an entity, and the
151
     *         key is the entity id.
152
     */
153
    public function getMetadataSet(string $set): array
154
    {
155
        if (array_key_exists($set, $this->cachedMetadata)) {
156
            return $this->cachedMetadata[$set];
157
        }
158
159
        $metadataSet = $this->load($set);
160
        if ($metadataSet === null) {
161
            $metadataSet = [];
162
        }
163
164
        // add the entity id of an entry to each entry in the metadata
165
        foreach ($metadataSet as $entityId => &$entry) {
166
            $entry['entityid'] = $entityId;
167
            // check we're not seeing the entityID from the metadata-template
168
            if ($set === 'saml20-idp-hosted') {
169
                Assert::notEq(
170
                    $entityId,
171
                    'urn:x-simplesamlphp:example-idp',
172
                    'Please set a valid and unique entityID',
173
                );
174
            }
175
        }
176
177
        $this->cachedMetadata[$set] = $metadataSet;
178
        return $metadataSet;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $metadataSet could return the type null which is incompatible with the type-hinted return array. Consider adding an additional type-check to rule them out.
Loading history...
179
    }
180
}
181