Passed
Push — master ( ec27ff...f3e803 )
by Tim
02:29
created

HTTPPost   A

Complexity

Total Complexity 10

Size/Duplication

Total Lines 89
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 38
dl 0
loc 89
rs 10
c 0
b 0
f 0
wmc 10

2 Methods

Rating   Name   Duplication   Size   Complexity  
A send() 0 34 5
A receive() 0 35 5
1
<?php
2
3
declare(strict_types=1);
4
5
namespace SimpleSAML\SAML2\Binding;
6
7
use Exception;
8
use Nyholm\Psr7\Response;
9
use Psr\Http\Message\ResponseInterface;
10
use Psr\Http\Message\ServerRequestInterface;
11
use SimpleSAML\Assert\Assert;
12
use SimpleSAML\SAML2\Binding;
13
use SimpleSAML\SAML2\Utils;
14
use SimpleSAML\SAML2\XML\samlp\AbstractMessage;
15
use SimpleSAML\SAML2\XML\samlp\AbstractRequest;
16
use SimpleSAML\SAML2\XML\samlp\MessageFactory;
17
use SimpleSAML\XML\DOMDocumentFactory;
18
19
use function array_key_exists;
20
use function base64_decode;
21
use function base64_encode;
22
23
/**
24
 * Class which implements the HTTP-POST binding.
25
 *
26
 * @package simplesamlphp/saml2
27
 */
28
class HTTPPost extends Binding
29
{
30
    /**
31
     * Send a SAML 2 message using the HTTP-POST binding.
32
     *
33
     * @param \SimpleSAML\SAML2\XML\samlp\AbstractMessage $message The message we should send.
34
     * @return \Psr\Http\Message\ResponseInterface The response
35
     */
36
    public function send(AbstractMessage $message): ResponseInterface
37
    {
38
        if ($this->destination === null) {
39
            $destination = $message->getDestination();
40
            if ($destination === null) {
41
                throw new Exception('Cannot send message, no destination set.');
42
            }
43
        } else {
44
            $destination = $this->destination;
45
        }
46
        $relayState = $this->getRelayState();
47
48
        $msgStr = $message->toXML();
49
50
        Utils::getContainer()->debugMessage($msgStr, 'out');
51
        $msgStr = $msgStr->ownerDocument?->saveXML($msgStr);
52
53
        $msgStr = base64_encode($msgStr);
54
55
        if ($message instanceof AbstractRequest) {
56
            $msgType = 'SAMLRequest';
57
        } else {
58
            $msgType = 'SAMLResponse';
59
        }
60
61
        $post = [];
62
        $post[$msgType] = $msgStr;
63
64
        if ($relayState !== null) {
65
            $post['RelayState'] = $relayState;
66
        }
67
68
        $container = Utils::getContainer();
69
        return new Response(303, ['Location' => $container->getPOSTRedirectURL($destination, $post)]);
70
    }
71
72
73
    /**
74
     * Receive a SAML 2 message sent using the HTTP-POST binding.
75
     *
76
     * Throws an exception if it is unable receive the message.
77
     *
78
     * @param \Psr\Http\Message\ServerRequestInterface $request
79
     * @return \SimpleSAML\SAML2\XML\samlp\AbstractMessage The received message.
80
     * @throws \Exception
81
     */
82
    public function receive(ServerRequestInterface $request): AbstractMessage
83
    {
84
        $query = $request->getParsedBody();
85
        if (array_key_exists('SAMLRequest', $query)) {
86
            $msgStr = $query['SAMLRequest'];
87
        } elseif (array_key_exists('SAMLResponse', $query)) {
88
            $msgStr = $query['SAMLResponse'];
89
        } else {
90
            throw new Exception('Missing SAMLRequest or SAMLResponse parameter.');
91
        }
92
93
        $msgStr = base64_decode($msgStr, true);
94
        $msgStr = DOMDocumentFactory::fromString($msgStr)->saveXML();
95
96
        $document = DOMDocumentFactory::fromString($msgStr);
97
        Utils::getContainer()->debugMessage($document->documentElement, 'in');
98
99
        $msg = MessageFactory::fromXML($document->documentElement);
100
101
        /**
102
         * 3.5.5.2 - SAML Bindings
103
         *
104
         * If the message is signed, the Destination XML attribute in the root SAML element of the protocol
105
         * message MUST contain the URL to which the sender has instructed the user agent to deliver the
106
         * message.
107
         */
108
        if ($msg->isSigned()) {
109
            Assert::notNull($msg->getDestination()); // Validation of the value must be done upstream
110
        }
111
112
        if (array_key_exists('RelayState', $query)) {
113
            $this->setRelayState($query['RelayState']);
114
        }
115
116
        return $msg;
117
    }
118
}
119