Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
 
3
/**
4
 * Licensed to Jasig under one or more contributor license
5
 * agreements. See the NOTICE file distributed with this work for
6
 * additional information regarding copyright ownership.
7
 *
8
 * Jasig licenses this file to you under the Apache License,
9
 * Version 2.0 (the "License"); you may not use this file except in
10
 * compliance with the License. You may obtain a copy of the License at:
11
 *
12
 * http://www.apache.org/licenses/LICENSE-2.0
13
 *
14
 * Unless required by applicable law or agreed to in writing, software
15
 * distributed under the License is distributed on an "AS IS" BASIS,
16
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
 * See the License for the specific language governing permissions and
18
 * limitations under the License.
19
 *
20
 * PHP Version 7
21
 *
22
 * @file     CAS/ProxiedService/Http/Abstract.php
23
 * @category Authentication
24
 * @package  PhpCAS
25
 * @author   Adam Franco <afranco@middlebury.edu>
26
 * @license  http://www.apache.org/licenses/LICENSE-2.0  Apache License 2.0
27
 * @link     https://wiki.jasig.org/display/CASC/phpCAS
28
 */
29
 
30
/**
31
 * This class implements common methods for ProxiedService implementations included
32
 * with phpCAS.
33
 *
34
 * @class    CAS_ProxiedService_Http_Abstract
35
 * @category Authentication
36
 * @package  PhpCAS
37
 * @author   Adam Franco <afranco@middlebury.edu>
38
 * @license  http://www.apache.org/licenses/LICENSE-2.0  Apache License 2.0
39
 * @link     https://wiki.jasig.org/display/CASC/phpCAS
40
 */
41
abstract class CAS_ProxiedService_Http_Abstract extends
42
CAS_ProxiedService_Abstract implements CAS_ProxiedService_Http
43
{
44
    /**
45
     * The HTTP request mechanism talking to the target service.
46
     *
47
     * @var CAS_Request_RequestInterface $requestHandler
48
     */
49
    protected $requestHandler;
50
 
51
    /**
52
     * The storage mechanism for cookies set by the target service.
53
     *
54
     * @var CAS_CookieJar $_cookieJar
55
     */
56
    private $_cookieJar;
57
 
58
    /**
59
     * Constructor.
60
     *
61
     * @param CAS_Request_RequestInterface $requestHandler request handler object
62
     * @param CAS_CookieJar                $cookieJar      cookieJar object
63
     *
64
     * @return void
65
     */
66
    public function __construct(CAS_Request_RequestInterface $requestHandler,
67
        CAS_CookieJar $cookieJar
68
    ) {
69
        $this->requestHandler = $requestHandler;
70
        $this->_cookieJar = $cookieJar;
71
    }
72
 
73
    /**
74
     * The target service url.
75
     * @var string $_url;
76
     */
77
    private $_url;
78
 
79
    /**
80
     * Answer a service identifier (URL) for whom we should fetch a proxy ticket.
81
     *
82
     * @return string
83
     * @throws Exception If no service url is available.
84
     */
85
    public function getServiceUrl()
86
    {
87
        if (empty($this->_url)) {
88
            throw new CAS_ProxiedService_Exception(
89
                'No URL set via ' . get_class($this) . '->setUrl($url).'
90
            );
91
        }
92
 
93
        return $this->_url;
94
    }
95
 
96
    /*********************************************************
97
     * Configure the Request
98
     *********************************************************/
99
 
100
    /**
101
     * Set the URL of the Request
102
     *
103
     * @param string $url url to set
104
     *
105
     * @return void
106
     * @throws CAS_OutOfSequenceException If called after the Request has been sent.
107
     */
108
    public function setUrl($url)
109
    {
110
        if ($this->hasBeenSent()) {
111
            throw new CAS_OutOfSequenceException(
112
                'Cannot set the URL, request already sent.'
113
            );
114
        }
115
        if (!is_string($url)) {
116
            throw new CAS_InvalidArgumentException('$url must be a string.');
117
        }
118
 
119
        $this->_url = $url;
120
    }
121
 
122
    /*********************************************************
123
     * 2. Send the Request
124
     *********************************************************/
125
 
126
    /**
127
     * Perform the request.
128
     *
129
     * @return void
130
     * @throws CAS_OutOfSequenceException If called multiple times.
131
     * @throws CAS_ProxyTicketException If there is a proxy-ticket failure.
132
     *		The code of the Exception will be one of:
133
     *			PHPCAS_SERVICE_PT_NO_SERVER_RESPONSE
134
     *			PHPCAS_SERVICE_PT_BAD_SERVER_RESPONSE
135
     *			PHPCAS_SERVICE_PT_FAILURE
136
     * @throws CAS_ProxiedService_Exception If there is a failure sending the
137
     * request to the target service.
138
     */
139
    public function send()
140
    {
141
        if ($this->hasBeenSent()) {
142
            throw new CAS_OutOfSequenceException(
143
                'Cannot send, request already sent.'
144
            );
145
        }
146
 
147
        phpCAS::traceBegin();
148
 
149
        // Get our proxy ticket and append it to our URL.
150
        $this->initializeProxyTicket();
151
        $url = $this->getServiceUrl();
152
        if (strstr($url, '?') === false) {
153
            $url = $url . '?ticket=' . $this->getProxyTicket();
154
        } else {
155
            $url = $url . '&ticket=' . $this->getProxyTicket();
156
        }
157
 
158
        try {
159
            $this->makeRequest($url);
160
        } catch (Exception $e) {
161
            phpCAS::traceEnd();
162
            throw $e;
163
        }
164
    }
165
 
166
    /**
167
     * Indicator of the number of requests (including redirects performed.
168
     *
169
     * @var int $_numRequests;
170
     */
171
    private $_numRequests = 0;
172
 
173
    /**
174
     * The response headers.
175
     *
176
     * @var array $_responseHeaders;
177
     */
178
    private $_responseHeaders = array();
179
 
180
    /**
181
     * The response status code.
182
     *
183
     * @var int $_responseStatusCode;
184
     */
185
    private $_responseStatusCode = '';
186
 
187
    /**
188
     * The response headers.
189
     *
190
     * @var string $_responseBody;
191
     */
192
    private $_responseBody = '';
193
 
194
    /**
195
     * Build and perform a request, following redirects
196
     *
197
     * @param string $url url for the request
198
     *
199
     * @return void
200
     * @throws CAS_ProxyTicketException If there is a proxy-ticket failure.
201
     *		The code of the Exception will be one of:
202
     *			PHPCAS_SERVICE_PT_NO_SERVER_RESPONSE
203
     *			PHPCAS_SERVICE_PT_BAD_SERVER_RESPONSE
204
     *			PHPCAS_SERVICE_PT_FAILURE
205
     * @throws CAS_ProxiedService_Exception If there is a failure sending the
206
     * request to the target service.
207
     */
208
    protected function makeRequest($url)
209
    {
210
        // Verify that we are not in a redirect loop
211
        $this->_numRequests++;
212
        if ($this->_numRequests > 4) {
213
            $message = 'Exceeded the maximum number of redirects (3) in proxied service request.';
214
            phpCAS::trace($message);
215
            throw new CAS_ProxiedService_Exception($message);
216
        }
217
 
218
        // Create a new request.
219
        $request = clone $this->requestHandler;
220
        $request->setUrl($url);
221
 
222
        // Add any cookies to the request.
223
        $request->addCookies($this->_cookieJar->getCookies($url));
224
 
225
        // Add any other parts of the request needed by concrete classes
226
        $this->populateRequest($request);
227
 
228
        // Perform the request.
229
        phpCAS::trace('Performing proxied service request to \'' . $url . '\'');
230
        if (!$request->send()) {
231
            $message = 'Could not perform proxied service request to URL`'
232
            . $url . '\'. ' . $request->getErrorMessage();
233
            phpCAS::trace($message);
234
            throw new CAS_ProxiedService_Exception($message);
235
        }
236
 
237
        // Store any cookies from the response;
238
        $this->_cookieJar->storeCookies($url, $request->getResponseHeaders());
239
 
240
        // Follow any redirects
241
        if ($redirectUrl = $this->getRedirectUrl($request->getResponseHeaders())
242
        ) {
243
            phpCAS::trace('Found redirect:' . $redirectUrl);
244
            $this->makeRequest($redirectUrl);
245
        } else {
246
 
247
            $this->_responseHeaders = $request->getResponseHeaders();
248
            $this->_responseBody = $request->getResponseBody();
249
            $this->_responseStatusCode = $request->getResponseStatusCode();
250
        }
251
    }
252
 
253
    /**
254
     * Add any other parts of the request needed by concrete classes
255
     *
256
     * @param CAS_Request_RequestInterface $request request interface object
257
     *
258
     * @return void
259
     */
260
    abstract protected function populateRequest(
261
        CAS_Request_RequestInterface $request
262
    );
263
 
264
    /**
265
     * Answer a redirect URL if a redirect header is found, otherwise null.
266
     *
267
     * @param array $responseHeaders response header to extract a redirect from
268
     *
269
     * @return string|null
270
     */
271
    protected function getRedirectUrl(array $responseHeaders)
272
    {
273
        // Check for the redirect after authentication
274
        foreach ($responseHeaders as $header) {
275
            if ( preg_match('/^(Location:|URI:)\s*([^\s]+.*)$/', $header, $matches)
276
            ) {
277
                return trim(array_pop($matches));
278
            }
279
        }
280
        return null;
281
    }
282
 
283
    /*********************************************************
284
     * 3. Access the response
285
     *********************************************************/
286
 
287
    /**
288
     * Answer true if our request has been sent yet.
289
     *
290
     * @return bool
291
     */
292
    protected function hasBeenSent()
293
    {
294
        return ($this->_numRequests > 0);
295
    }
296
 
297
    /**
298
     * Answer the headers of the response.
299
     *
300
     * @return array An array of header strings.
301
     * @throws CAS_OutOfSequenceException If called before the Request has been sent.
302
     */
303
    public function getResponseHeaders()
304
    {
305
        if (!$this->hasBeenSent()) {
306
            throw new CAS_OutOfSequenceException(
307
                'Cannot access response, request not sent yet.'
308
            );
309
        }
310
 
311
        return $this->_responseHeaders;
312
    }
313
 
314
    /**
315
     * Answer HTTP status code of the response
316
     *
317
     * @return int
318
     * @throws CAS_OutOfSequenceException If called before the Request has been sent.
319
     */
320
    public function getResponseStatusCode()
321
    {
322
        if (!$this->hasBeenSent()) {
323
            throw new CAS_OutOfSequenceException(
324
                'Cannot access response, request not sent yet.'
325
            );
326
        }
327
 
328
        return $this->_responseStatusCode;
329
    }
330
 
331
    /**
332
     * Answer the body of response.
333
     *
334
     * @return string
335
     * @throws CAS_OutOfSequenceException If called before the Request has been sent.
336
     */
337
    public function getResponseBody()
338
    {
339
        if (!$this->hasBeenSent()) {
340
            throw new CAS_OutOfSequenceException(
341
                'Cannot access response, request not sent yet.'
342
            );
343
        }
344
 
345
        return $this->_responseBody;
346
    }
347
 
348
    /**
349
     * Answer the cookies from the response. This may include cookies set during
350
     * redirect responses.
351
     *
352
     * @return array An array containing cookies. E.g. array('name' => 'val');
353
     */
354
    public function getCookies()
355
    {
356
        return $this->_cookieJar->getCookies($this->getServiceUrl());
357
    }
358
 
359
}
360
?>