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
Bug
introduced
by
![]() |
|||||
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
![]() |
|||||
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
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. ![]() |
|||||
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
![]() |
|||||
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 |