Authstack SAML Service Provider : code coverage of AuthStack\SAMLServiceProvider\Model\Logout

# Code coverage of Authstack SAML Service Provider: AuthStack\SAMLServiceProvider\Model\Logout

## Class code coverage 39.39%

### Methods

- 80% __construct
- 100% receive
- 100% getLogoutRequest
- 100% getLogoutResponse
- 0% getLightSamlLogoutResponse
- 50% validateLogoutRequest
- 0% send
- 0% create
- 0% sendWith

### Source

Line | Code
--- | ---
1 |
    <?php
2 |
3 |
    namespace AuthStack\SAMLServiceProvider\Model;
4 |
5 |
    use LightSaml\Helper;
6 |
    use LightSaml\SamlConstants;
7 |
    use Illuminate\Http\Request;
8 |
    use LightSaml\Model\Assertion\Issuer;
9 |
    use LightSaml\Binding\AbstractBinding;
10 |
    use LightSaml\Binding\BindingFactory;
11 |
    use LightSaml\Binding\HttpPostBinding;
12 |
    use LightSaml\Binding\HttpRedirectBinding;
13 |
    use LightSaml\Model\Protocol\LogoutRequest;
14 |
    use LightSaml\Model\Protocol\LogoutResponse;
15 |
    use LightSaml\Context\Profile\MessageContext;
16 |
    use LightSaml\Model\XmlDSig\SignatureXmlReader;
17 |
    use LightSaml\Model\XmlDSig\SignatureStringReader;
18 |
    use AuthStack\SAMLServiceProvider\Exceptions\SAMLException;
19 |
    use AuthStack\SAMLServiceProvider\Model\LogoutResponse as LogoutResp;
20 |
21 |
    class Logout
22 |
    {
23 |
        /**
24 |
         * @var ServiceProvider
25 |
         */
26 |
        protected $sp;
27 |
28 |
        /**
29 |
         * @var IdentityProvider
30 |
         */
31 |
        protected $idp;
32 |
33 |
        /**
34 |
         * @var LogoutRequest
35 |
         */
36 |
        protected $logout_request;
37 |
38 |
        /**
39 |
         * @var LogoutResponse
40 |
         */
41 |
        protected $logout_response;
42 |
43 |
        /**
44 |
         * Logout constructor.
45 |
         * @param bool $create
46 |
         */
47 |
        public function __construct(bool $create = false)
48 |
        {
49 |
            $this->sp = new ServiceProvider();
50 |
51 |
            $this->idp = new IdentityProvider($this->sp->getIdPEntityId());
52 |
53 |
            if($create)
54 |
            {
55 |
                $this->create();
56 |
            }
57 |
        }
58 |
59 |
        /**
60 |
         * @param Request|null $request
61 |
         * @return $this
62 |
         */
63 |
        public function receive(Request $request = null): self
64 |
        {
65 |
            $request = $request ?? Request::createFromGlobals();
66 |
67 |
            $factory = new BindingFactory();
68 |
            $context = new MessageContext();
69 |
70 |
            $binding = $factory->getBindingByRequest($request);
71 |
72 |
            $binding->receive($request, $context);
73 |
74 |
            $this->logout_request = $context->getMessage();
75 |
76 |
            return $this;
77 |
        }
78 |
79 |
        /**
80 |
         * @return LogoutRequest
81 |
         */
82 |
        public function getLogoutRequest(): LogoutRequest
83 |
        {
84 |
            return $this->logout_request;
85 |
        }
86 |
87 |
        /**
88 |
         * @return \AuthStack\SAMLServiceProvider\Model\LogoutResponse
89 |
         */
90 |
        public function getLogoutResponse()
91 |
        {
92 |
            return new LogoutResp($this->getLogoutRequest(), $this->sp, $this->idp);
93 |
        }
94 |
95 |
        /**
96 |
         * @return LogoutResponse
97 |
         */
98 |
        public function getLightSamlLogoutResponse(): LogoutResponse
99 |
        {
100 |
            return $this->logout_response;
101 |
        }
102 |
103 |
        /**
104 |
         * @return bool
105 |
         * @throws SAMLException
106 |
         */
107 |
        public function validateLogoutRequest(): bool
108 |
        {
109 |
            $request = $this->getLogoutRequest();
110 |
111 |
            //--------------------------------------------------------------------------------------------------------------
112 |
            // Validate IdP's signature. This is the most important of all security aspects.
113 |
            //--------------------------------------------------------------------------------------------------------------
114 |
            if($this->idp->wantsSignedReguests())
115 |
            {
116 |
                /** @var  $signature SignatureXmlReader|SignatureStringReader */
117 |
                $signature = $request->getSignature();
118 |
119 |
                $pubkey = $this->sp->getSigningPublicKey();
120 |
121 |
                try
122 |
                {
123 |
                    $signature->validate($pubkey);
124 |
                }
125 |
                catch(\Exception $e)
126 |
                {
127 |
                    throw new SAMLException($e->getMessage(), 400);
128 |
                }
129 |
            }
130 |
131 |
            //--------------------------------------------------------------------------------------------------------------
132 |
            // Check the issuer. EntityId in the response MUST be equal to EntityId in IdentityProvider::class
133 |
            //--------------------------------------------------------------------------------------------------------------
134 |
            if(!(0 === strcmp($request->getIssuer()->getValue(), $this->sp->getEntityID())))
135 |
            {
136 |
                throw new SAMLException(
137 |
                    sprintf("Invalid issuer. Expected %s, received: %s", $this->sp->getEntityID(), $request->getIssuer()->getValue()),
138 |
                    400
139 |
                );
140 |
            }
141 |
142 |
            //--------------------------------------------------------------------------------------------------------------
143 |
            // Check whether the destination matches
144 |
            //--------------------------------------------------------------------------------------------------------------
145 |
            if(!(0 === strcmp($request->getDestination(), $this->idp->getLogoutURL())))
146 |
            {
147 |
                throw new SAMLException(
148 |
                    sprintf("Invalid destination. Expected %s, received: %s", $this->idp->getLogoutURL(), $request->getDestination()),
149 |
                    400
150 |
                );
151 |
            }
152 |
153 |
            //--------------------------------------------------------------------------------------------------------------
154 |
            // Check for the issued at time and compare it to allowed boundaries
155 |
            //--------------------------------------------------------------------------------------------------------------
156 |
            $iat = $request->getIssueInstantDateTime()->setTimezone(new \DateTimeZone('UTC'));
157 |
            $upper = $this->idp->getUpperDT();
158 |
            $lower = $this->idp->getLowerDT();
159 |
160 |
            if($iat > $upper)
161 |
            {
162 |
                throw new SAMLException(
163 |
                    sprintf("Assertion valid in the future. Check the time synchronization between IdP and SP. Issued at: %s, checked against: %s",
164 |
                        $iat->format('c'), $upper->format('c')),
165 |
                    400);
166 |
            }
167 |
168 |
            if($iat < $lower)
169 |
            {
170 |
                throw new SAMLException("Assertion expired. Issued at: %s, now: %s", $iat->format('c'), $lower->format('c'));
171 |
            }
172 |
173 |
            return true;
174 |
        }
175 |
176 |
        /**
177 |
         * @throws SAMLException
178 |
         */
179 |
        public function send()
180 |
        {
181 |
            switch($this->idp->getLogoutBinding())
182 |
            {
183 |
                case SamlConstants::BINDING_SAML2_HTTP_POST:
184 |
                    $this->sendWith(new HttpPostBinding());
185 |
                    break;
186 |
187 |
                case SamlConstants::BINDING_SAML2_HTTP_REDIRECT:
188 |
                    $this->sendWith(new HttpRedirectBinding());
189 |
                    break;
190 |
191 |
                default:
192 |
                    throw new SAMLException(sprintf("Unsupported binding: ", $this->idp->getLogoutBinding()), 500);
193 |
                    break;
194 |
            }
195 |
        }
196 |
197 |
        /**
198 |
         * @return Logout
199 |
         */
200 |
        protected function create(): self
201 |
        {
202 |
            $request = new LogoutRequest();
203 |
            $request_id = Helper::generateID();
204 |
205 |
            $request
206 |
                ->setID($request_id)
207 |
                ->setDestination($this->idp->getLogoutURL())
208 |
                ->setIssueInstant(time())
209 |
                ->setIssuer(new Issuer($this->sp->getEntityId()))
210 |
            ;
211 |
212 |
            if($this->idp->wantsSignedReguests())
213 |
            {
214 |
                $request->setSignature($this->idp->getSignatureWriter());
215 |
            }
216 |
217 |
            $this->logout_request = $request;
218 |
219 |
            return $this;
220 |
        }
221 |
222 |
        /**
223 |
         * @param AbstractBinding $binding
224 |
         */
225 |
        protected  function sendWith(AbstractBinding $binding)
226 |
        {
227 |
            $request = $this->getLogoutRequest();
228 |
229 |
            $context = new MessageContext();
230 |
231 |
            $context->setMessage($request);
232 |
233 |
            $binding->send($context)->send();
234 |
        }
235 |
    }