Issues (4)

src/Controller/MetaEditor.php (4 issues)

1
<?php
2
3
declare(strict_types=1);
4
5
namespace SimpleSAML\Module\metaedit\Controller;
6
7
use Exception;
8
use SimpleSAML\SAML2\Constants as C;
9
use SimpleSAML\Auth;
10
use SimpleSAML\Configuration;
11
use SimpleSAML\Error;
12
use SimpleSAML\Metadata;
13
use SimpleSAML\Module\metaedit\MetaEditor as Editor;
14
use SimpleSAML\Session;
15
use SimpleSAML\Utils;
16
use SimpleSAML\XHTML\Template;
17
use Symfony\Component\HttpFoundation\Request;
18
19
use function array_key_exists;
20
use function array_pop;
21
22
/**
23
 * Controller class for the metaedit module.
24
 *
25
 * This class serves the different views available in the module.
26
 *
27
 * @package simplesamlphp/simplesamlphp-module-metaedit
28
 */
29
class MetaEditor
30
{
31
    /** @var \SimpleSAML\Configuration */
32
    protected Configuration $config;
33
34
    /** @var \SimpleSAML\Configuration */
35
    protected Configuration $moduleConfig;
36
37
    /** @var \SimpleSAML\Session */
38
    protected Session $session;
39
40
    /**
41
     * @var \SimpleSAML\Auth\Simple|string
42
     * @psalm-var \SimpleSAML\Auth\Simple|class-string
43
     */
44
    protected $authSimple = Auth\Simple::class;
45
46
47
    /**
48
     * Controller constructor.
49
     *
50
     * It initializes the global configuration and session for the controllers implemented here.
51
     *
52
     * @param \SimpleSAML\Configuration $config The configuration to use by the controllers.
53
     * @param \SimpleSAML\Session $session The session to use by the controllers.
54
     *
55
     * @throws \Exception
56
     */
57
    public function __construct(
58
        Configuration $config,
59
        Session $session,
60
    ) {
61
        $this->config = $config;
62
        $this->moduleConfig = Configuration::getConfig('module_metaedit.php');
63
        $this->session = $session;
64
    }
65
66
67
    /**
68
     * Inject the \SimpleSAML\Auth\Simple dependency.
69
     *
70
     * @param \SimpleSAML\Auth\Simple $authSimple
71
     */
72
    public function setAuthSimple(Auth\Simple $authSimple): void
73
    {
74
        $this->authSimple = $authSimple;
75
    }
76
77
78
    /**
79
     * Main index
80
     *
81
     * @param \Symfony\Component\HttpFoundation\Request $request The current request.
82
     *
83
     * @return \SimpleSAML\XHTML\Template
84
     */
85
    public function main(Request $request): Template
86
    {
87
        $authsource = $this->moduleConfig->getOptionalValue('auth', 'login-admin');
88
        $useridattr = $this->moduleConfig->getOptionalValue('useridattr', 'eduPersonPrincipalName');
89
90
        $as = new $this->authSimple($authsource);
91
        $as->requireAuth();
92
        $attributes = $as->getAttributes();
93
94
        // Check if userid exists
95
        if (!isset($attributes[$useridattr])) {
96
            throw new Error\Exception('User ID is missing');
97
        }
98
        $userid = $attributes[$useridattr][0];
99
100
        $mdh = new Metadata\MetaDataStorageHandlerSerialize(
101
            $this->moduleConfig->getOptionalArray('metahandlerConfig', ['directory' => '']),
0 ignored issues
show
It seems like $this->moduleConfig->get...ray('directory' => '')) can also be of type null; however, parameter $config of SimpleSAML\Metadata\Meta...erialize::__construct() 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

101
            /** @scrutinizer ignore-type */ $this->moduleConfig->getOptionalArray('metahandlerConfig', ['directory' => '']),
Loading history...
102
        );
103
104
        $delete = $request->get('delete');
105
        if ($delete !== null) {
106
            $premetadata = $mdh->getMetadata($delete, 'saml20-sp-remote');
107
            $this->requireOwnership($premetadata, $userid);
0 ignored issues
show
It seems like $premetadata can also be of type null; however, parameter $metadata of SimpleSAML\Module\metaed...tor::requireOwnership() 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

107
            $this->requireOwnership(/** @scrutinizer ignore-type */ $premetadata, $userid);
Loading history...
108
            $mdh->deleteMetadata($delete, 'saml20-sp-remote');
109
        }
110
111
        $list = $mdh->getMetadataSet('saml20-sp-remote');
112
113
        $slist = ['mine' => [], 'others' => []];
114
        foreach ($list as $listitem) {
115
            if (array_key_exists('owner', $listitem)) {
116
                if ($listitem['owner'] === $userid) {
117
                    $slist['mine'][] = $listitem;
118
                    continue;
119
                }
120
            }
121
            $slist['others'][] = $listitem;
122
        }
123
124
        $t = new Template($this->config, 'metaedit:metalist.twig');
125
        $t->data['metadata'] = $slist;
126
        $t->data['userid'] = $userid;
127
128
        return $t;
129
    }
130
131
132
    /**
133
     * Editor
134
     *
135
     * @param \Symfony\Component\HttpFoundation\Request $request The current request.
136
     *
137
     * @return \SimpleSAML\XHTML\Template
138
     */
139
    public function edit(Request $request): Template
140
    {
141
        $authsource = $this->moduleConfig->getOptionalValue('auth', 'login-admin');
142
        $useridattr = $this->moduleConfig->getOptionalValue('useridattr', 'eduPersonPrincipalName');
143
144
        $as = new $this->authSimple($authsource);
145
        $as->requireAuth();
146
147
        $attributes = $as->getAttributes();
148
        // Check if userid exists
149
        if (!isset($attributes[$useridattr])) {
150
            throw new Error\Exception('User ID is missing');
151
        }
152
        $userid = $attributes[$useridattr][0];
153
154
        $entityId = $request->get('entityid');
155
        $xmlMetadata = $request->get('xmlmetadata');
156
157
        $mdh = new Metadata\MetaDataStorageHandlerSerialize($this->moduleConfig->getArray('metahandlerConfig', []));
0 ignored issues
show
The call to SimpleSAML\Configuration::getArray() has too many arguments starting with array(). ( Ignorable by Annotation )

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

157
        $mdh = new Metadata\MetaDataStorageHandlerSerialize($this->moduleConfig->/** @scrutinizer ignore-call */ getArray('metahandlerConfig', []));

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
158
159
        if ($entityId !== null) {
160
            $metadata = $mdh->getMetadata($entityId, 'saml20-sp-remote');
161
            $this->requireOwnership($metadata, $userid);
0 ignored issues
show
It seems like $metadata can also be of type null; however, parameter $metadata of SimpleSAML\Module\metaed...tor::requireOwnership() 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

161
            $this->requireOwnership(/** @scrutinizer ignore-type */ $metadata, $userid);
Loading history...
162
        } elseif ($xmlMetadata !== null) {
163
            $xmlUtils = new Utils\XML();
164
            $xmlUtils->checkSAMLMessage($xmlMetadata, 'saml-meta');
165
            $entities = Metadata\SAMLParser::parseDescriptorsString($xmlMetadata);
166
            $entity = array_pop($entities);
167
            $metadata = $entity->getMetadata20SP();
168
169
            /* Trim metadata endpoint arrays. */
170
            $metadata['AssertionConsumerService'] = [
171
                Utils\Config\Metadata::getDefaultEndpoint(
172
                    $metadata['AssertionConsumerService'],
173
                    [C::BINDING_HTTP_POST],
174
                ),
175
            ];
176
            $metadata['SingleLogoutService'] = [
177
                Utils\Config\Metadata::getDefaultEndpoint(
178
                    $metadata['SingleLogoutService'],
179
                    [C::BINDING_HTTP_REDIRECT],
180
                ),
181
            ];
182
        } else {
183
            $metadata = [
184
                'owner' => $userid,
185
            ];
186
        }
187
188
        $editor = new Editor();
189
190
        if ($request->get('submit')) {
191
            $editor->checkForm($request->request->all());
192
            $metadata = $editor->formToMeta($request->request->all(), [], ['owner' => $userid]);
193
            $wasEntityId = $request->get('was-entityid');
194
            if (($wasEntityId !== null) && ($wasEntityId !== $metadata['entityid'])) {
195
                $premetadata = $mdh->getMetadata($wasEntityId, 'saml20-sp-remote');
196
                $this->requireOwnership($premetadata, $userid);
197
                $mdh->deleteMetadata($wasEntityId, 'saml20-sp-remote');
198
            }
199
200
            try {
201
                $testmetadata = $mdh->getMetadata($metadata['entityid'], 'saml20-sp-remote');
202
            } catch (Exception $e) {
203
                // catch
204
                $testmetadata = null;
205
            }
206
207
            if ($testmetadata) {
208
                $this->requireOwnership($testmetadata, $userid);
209
            }
210
211
            $result = $mdh->saveMetadata($metadata['entityid'], 'saml20-sp-remote', $metadata);
212
            if ($result === false) {
213
                throw new Error\Exception("Could not save metadata. See log for details");
214
            }
215
216
            return new Template($this->config, 'metaedit:saved.twig');
217
        }
218
219
        $form = $editor->metaToForm($metadata);
220
221
        $t = new Template($this->config, 'metaedit:formedit.twig');
222
        $t->data['form'] = $form;
223
224
        return $t;
225
    }
226
227
228
    /**
229
     * Importer
230
     *
231
     * @return \SimpleSAML\XHTML\Template
232
     */
233
    public function import(): Template
234
    {
235
        /* Load simpleSAMLphp, configuration and metadata */
236
        return new Template($this->config, 'metaedit:xmlimport.twig');
237
    }
238
239
240
    /**
241
     * @param array $metadata
242
     * @param string $userid
243
     * @return void
244
     */
245
    private function requireOwnership(array $metadata, string $userid): void
246
    {
247
        if (!isset($metadata['owner'])) {
248
            throw new Exception('Metadata has no owner. Which means no one is granted access, not even you.');
249
        }
250
251
        if ($metadata['owner'] !== $userid) {
252
            throw new Exception(
253
                'Metadata has an owner that is not equal to your userid, hence you are not granted access.',
254
            );
255
        }
256
    }
257
}
258