LogoutReceiveRequest::receiveRequest()   A
last analyzed

Complexity

Conditions 3
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 10
rs 9.4286
cc 3
eloc 5
nc 2
nop 1
1
<?php
2
3
namespace AerialShip\SamlSPBundle\Bridge;
4
5
use AerialShip\LightSaml\Helper;
6
use AerialShip\LightSaml\Meta\SerializationContext;
7
use AerialShip\LightSaml\Model\Metadata\KeyDescriptor;
8
use AerialShip\LightSaml\Model\Metadata\Service\SingleLogoutService;
9
use AerialShip\LightSaml\Model\Protocol\LogoutRequest;
10
use AerialShip\LightSaml\Model\Protocol\LogoutResponse;
11
use AerialShip\LightSaml\Model\Protocol\Status;
12
use AerialShip\LightSaml\Model\XmlDSig\SignatureValidatorInterface;
13
use AerialShip\LightSaml\Security\KeyHelper;
14
use AerialShip\SamlSPBundle\Config\ServiceInfo;
15
use AerialShip\SamlSPBundle\Config\ServiceInfoCollection;
16
use AerialShip\SamlSPBundle\RelyingParty\RelyingPartyInterface;
17
use AerialShip\SamlSPBundle\State\SSO\SSOStateStoreInterface;
18
use Symfony\Component\HttpFoundation\Request;
19
use Symfony\Component\HttpFoundation\Response;
20
use Symfony\Component\Security\Http\HttpUtils;
21
22
class LogoutReceiveRequest extends LogoutBase implements RelyingPartyInterface
23
{
24
    /** @var BindingManager */
25
    protected $bindingManager;
26
27
    /** @var ServiceInfoCollection  */
28
    protected $serviceInfoCollection;
29
30
31
32
    public function __construct(
33
        BindingManager $bindingManager,
34
        SSOStateStoreInterface $ssoStore,
35
        ServiceInfoCollection $serviceInfoCollection,
36
        HttpUtils $httpUtils
37
    ) {
38
        parent::__construct($ssoStore, $httpUtils);
39
        $this->bindingManager = $bindingManager;
40
        $this->serviceInfoCollection = $serviceInfoCollection;
41
    }
42
43
44
    /**
45
     * @param \Symfony\Component\HttpFoundation\Request $request
46
     * @return bool
47
     */
48
    public function supports(Request $request)
49
    {
50
        if ($request->attributes->get('logout_path') != $request->getPathInfo()) {
51
            return false;
52
        }
53
        if (!$request->get('SAMLRequest')) {
54
            return false;
55
        }
56
        return true;
57
    }
58
59
    /**
60
     * @param \Symfony\Component\HttpFoundation\Request $request
61
     * @throws \RuntimeException
62
     * @throws \InvalidArgumentException if cannot manage the Request
63
     * @return \Symfony\Component\HttpFoundation\Response|SamlSpInfo|null
64
     */
65
    public function manage(Request $request)
66
    {
67
        if (!$this->supports($request)) {
68
            throw new \InvalidArgumentException('Unsupported request');
69
        }
70
71
        $logoutRequest = $this->receiveRequest($request);
72
        $serviceInfo = $this->getServiceInfo($logoutRequest);
73
        $this->validateLogoutRequest($serviceInfo, $logoutRequest);
74
        $arrStates = $this->getSSOState($serviceInfo, $logoutRequest->getNameID()->getValue(), $logoutRequest->getSessionIndex());
75
        $this->deleteSSOState($arrStates);
76
77
        $logoutResponse = new LogoutResponse();
78
        $logoutResponse->setID(Helper::generateID());
79
        $logoutResponse->setIssuer($serviceInfo->getSpProvider()->getEntityDescriptor()->getEntityID());
80
        $logoutResponse->setInResponseTo($logoutRequest->getID());
81
82
        $arrSLO = $serviceInfo->getIdpProvider()->getEntityDescriptor()->getFirstIdpSsoDescriptor()->findSingleLogoutServices();
83
        /** @var  $slo SingleLogoutService */
84
        $slo = array_pop($arrSLO);
85
        $logoutResponse->setDestination($slo->getLocation());
86
87
        $status = new Status();
88
        $status->setSuccess();
89
        $logoutResponse->setStatus($status);
90
91
        $context = new SerializationContext();
92
        $logoutResponse->getXml($context->getDocument(), $context);
93
        $xml = $context->getDocument()->saveXML();
94
95
        return new Response($xml, 200, array('Content-Type' => 'application/xml'));
96
    }
97
98
99
    /**
100
     * @param Request $request
101
     * @return LogoutRequest
102
     * @throws \InvalidArgumentException
103
     */
104
    protected function receiveRequest(Request $request)
105
    {
106
        /** @var  $logoutRequest LogoutRequest */
107
        $logoutRequest = $this->bindingManager->receive($request);
108
        if (!$logoutRequest || !$logoutRequest instanceof LogoutRequest) {
109
            throw new \InvalidArgumentException('Did not receive logout request');
110
        }
111
112
        return $logoutRequest;
113
    }
114
115
116
    /**
117
     * @param LogoutRequest $logoutRequest
118
     * @return ServiceInfo|null
119
     * @throws \RuntimeException
120
     */
121
    protected function getServiceInfo(LogoutRequest $logoutRequest)
122
    {
123
        $serviceInfo = $this->serviceInfoCollection->findByIDPEntityID($logoutRequest->getIssuer());
124
        if (!$serviceInfo) {
125
            throw new \RuntimeException('Got logout request from unknown IDP: '.$logoutRequest->getIssuer());
126
        }
127
128
        return $serviceInfo;
129
    }
130
131
    /**
132
     * @param \AerialShip\SamlSPBundle\Config\ServiceInfo $serviceInfo
133
     * @param LogoutRequest $logoutRequest
134
     * @throws \RuntimeException
135
     */
136
    protected function validateLogoutRequest(ServiceInfo $serviceInfo, LogoutRequest $logoutRequest)
137
    {
138
        $idp = $serviceInfo->getIdpProvider()->getEntityDescriptor();
139
        $keyDescriptors = $idp->getFirstIdpSsoDescriptor()->getKeyDescriptors();
140
        if (empty($keyDescriptors)) {
141
            throw new \RuntimeException('IDP must support signing for logout requests');
142
        }
143
144
        /** @var  $signature SignatureValidatorInterface */
145
        $signature = $logoutRequest->getSignature();
146
        if (!$signature) {
147
            throw new \RuntimeException('Logout request must be signed');
148
        }
149
150
        $keys = array();
151
        foreach ($keyDescriptors as $keyDescriptor) {
152
            $key = KeyHelper::createPublicKey($keyDescriptor->getCertificate());
153
            $keys[] = $key;
154
        }
155
156
        $signature->validateMulti($keys);
157
    }
158
}
159