1 | <?php |
||
2 | |||
3 | /** |
||
4 | * Copyright 2020 SURFnet bv |
||
5 | * |
||
6 | * Licensed under the Apache License, Version 2.0 (the "License"); |
||
7 | * you may not use this file except in compliance with the License. |
||
8 | * You may obtain a copy of the License at |
||
9 | * |
||
10 | * http://www.apache.org/licenses/LICENSE-2.0 |
||
11 | * |
||
12 | * Unless required by applicable law or agreed to in writing, software |
||
13 | * distributed under the License is distributed on an "AS IS" BASIS, |
||
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||
15 | * See the License for the specific language governing permissions and |
||
16 | * limitations under the License. |
||
17 | */ |
||
18 | |||
19 | namespace Surfnet\StepupGateway\Behat\Controller; |
||
20 | |||
21 | use Exception; |
||
22 | use Psr\Log\LoggerInterface; |
||
23 | use RobRichards\XMLSecLibs\XMLSecurityKey; |
||
24 | use RuntimeException; |
||
25 | use SAML2\AuthnRequest; |
||
26 | use SAML2\Certificate\PrivateKeyLoader; |
||
27 | use SAML2\Configuration\PrivateKey; |
||
28 | use SAML2\Constants; |
||
29 | use SAML2\HTTPPost; |
||
30 | use SAML2\HTTPRedirect; |
||
31 | use SAML2\Response as SAMLResponse; |
||
32 | use SAML2\XML\saml\Issuer; |
||
33 | use SAML2\XML\saml\NameID; |
||
34 | use Surfnet\SamlBundle\Http\XMLResponse; |
||
35 | use Surfnet\StepupGateway\Behat\Repository\SamlEntityRepository; |
||
36 | use Surfnet\StepupGateway\Behat\ServiceProviderContext; |
||
37 | use Surfnet\StepupGateway\SecondFactorOnlyBundle\Adfs\ValueObject\Response as AdfsResponse; |
||
38 | use Symfony\Component\HttpFoundation\Request; |
||
39 | use Symfony\Component\HttpFoundation\Response; |
||
40 | use Twig\Environment; |
||
41 | |||
42 | class ServiceProviderController |
||
43 | { |
||
44 | private $twig; |
||
45 | |||
46 | private $logger; |
||
47 | |||
48 | public function __construct(Environment $twig, LoggerInterface $logger) |
||
49 | { |
||
50 | $this->logger = $logger; |
||
51 | $this->twig = $twig; |
||
52 | } |
||
53 | |||
54 | /** |
||
55 | * Simply dumps the SAMLResponse XML |
||
56 | */ |
||
57 | public function acsAction(Request $request) |
||
58 | { |
||
59 | $this->logger->notice('Getting ready to consume the assertion on the test SP'); |
||
60 | $isAdfs = false; |
||
61 | if ($request->request->has('_SAMLResponse')) { |
||
62 | $this->logger->notice('Handling a test ADFS assertion'); |
||
63 | // The ADFS saml response is hidden in the _SAMLResponse input, in order to get the |
||
64 | $request->request->set('SAMLResponse', $request->request->get('_SAMLResponse')); |
||
65 | $_POST['SAMLResponse'] = $request->request->get('_SAMLResponse'); |
||
66 | $isAdfs = true; |
||
67 | } |
||
68 | try { |
||
69 | $this->logger->notice('Process the assertion on the test SP (try POST binding)'); |
||
70 | $httpPostBinding = new HTTPPost(); |
||
71 | $message = $httpPostBinding->receive(); |
||
72 | } catch (Exception $e1) { |
||
73 | try { |
||
74 | $this->logger->alert('Processing failed on the test SP'); |
||
75 | $httpRedirectBinding = new HTTPRedirect(); |
||
76 | $message = $httpRedirectBinding->receive(); |
||
77 | } catch (Exception) { |
||
78 | throw new RuntimeException('Unable to retrieve SAML message?', 1, $e1); |
||
79 | } |
||
80 | } |
||
81 | |||
82 | if (!$message instanceof SAMLResponse) { |
||
83 | throw new RuntimeException(sprintf('Unrecognized message type received: "%s"', get_class($message))); |
||
84 | } |
||
85 | |||
86 | $xml = base64_decode($request->get('SAMLResponse')); |
||
87 | $this->logger->notice(sprintf('Received SAMLResponse with status "%s"', implode($message->getStatus()))); |
||
88 | $this->logger->notice('The XML received', ['response-xml' => $xml]); |
||
89 | |||
90 | if ($isAdfs) { |
||
91 | $html = $this->twig->render( |
||
92 | '@test_resources/adfs_acs.html.twig', |
||
93 | [ |
||
94 | 'samlResponse' => $xml, |
||
95 | 'context' => $request->request->get('Context'), |
||
96 | 'authMethod' => $request->request->get('AuthMethod'), |
||
97 | ] |
||
98 | ); |
||
99 | return new Response($html); |
||
100 | } |
||
101 | return new XMLResponse($xml); |
||
102 | } |
||
103 | |||
104 | /** |
||
105 | * Posts an authn request to the SA Gateway, adding two additional |
||
106 | * parameters to the POST in addition to those found on a regular |
||
107 | * authn request (AuthNRequest and RelayState) |
||
108 | */ |
||
109 | public function adfsSsoAction(Request $request) |
||
110 | { |
||
111 | $nameId = $request->get('nameId'); |
||
112 | $loa = $request->get('loa'); |
||
113 | $entityId = $request->get('entityId'); |
||
114 | |||
115 | $authnRequest = new AuthnRequest(); |
||
116 | // In order to later assert if the response succeeded or failed, set our own dummy ACS location |
||
117 | $authnRequest->setAssertionConsumerServiceURL(SamlEntityRepository::SP_ACS_LOCATION); |
||
118 | $issuerVo = new Issuer(); |
||
119 | $issuerVo->setValue($entityId); |
||
120 | $authnRequest->setIssuer($issuerVo); |
||
121 | $authnRequest->setDestination(ServiceProviderContext::SFO_ENDPOINT_URL); |
||
122 | $authnRequest->setProtocolBinding(Constants::BINDING_HTTP_REDIRECT); |
||
123 | |||
124 | $nameIdVo = new NameID(); |
||
125 | $nameIdVo->setValue($nameId); |
||
126 | $nameIdVo->setFormat(Constants::NAMEFORMAT_UNSPECIFIED); |
||
127 | |||
128 | $authnRequest->setNameId($nameIdVo); |
||
129 | |||
130 | $keyLoader = new PrivateKeyLoader(); |
||
131 | $privateKey = $keyLoader->loadPrivateKey(new PrivateKey('/config/ssp/sp.key', 'default')); |
||
132 | $key = new XMLSecurityKey(XMLSecurityKey::RSA_SHA256, ['type' => 'private']); |
||
133 | $key->loadKey($privateKey->getKeyAsString()); |
||
134 | |||
135 | $authnRequest->setSignatureKey($key); |
||
136 | $authnRequest->setRequestedAuthnContext( |
||
137 | ['AuthnContextClassRef' => [$loa]] |
||
138 | ); |
||
139 | |||
140 | $context = '<EncryptedData></EncryptedData>'; |
||
141 | $authMethod = 'ADFS.SCSA'; |
||
142 | $arXml = $authnRequest->toSignedXML(); |
||
143 | $arBase64Encoded = base64_encode($arXml->ownerDocument->saveXml($arXml)); |
||
0 ignored issues
–
show
|
|||
144 | $response = $this->twig->render( |
||
145 | '@test_resources/adfs_login.html.twig', |
||
146 | [ |
||
147 | 'ssoUrl' => $authnRequest->getDestination(), |
||
148 | 'authNRequest' => $arBase64Encoded, |
||
149 | 'adfs' => AdfsResponse::fromValues($authMethod, $context) |
||
150 | ] |
||
151 | ); |
||
152 | |||
153 | return new Response($response); |
||
154 | } |
||
155 | } |
||
156 |
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.
This is most likely a typographical error or the method has been renamed.