123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361 |
- <?php
- /**
- * Licensed to Jasig under one or more contributor license
- * agreements. See the NOTICE file distributed with this work for
- * additional information regarding copyright ownership.
- *
- * Jasig licenses this file to you under the Apache License,
- * Version 2.0 (the "License"); you may not use this file except in
- * compliance with the License. You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * PHP Version 5
- *
- * @file CAS/ProxiedService/Http/Abstract.php
- * @category Authentication
- * @package PhpCAS
- * @author Adam Franco <afranco@middlebury.edu>
- * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
- * @link https://wiki.jasig.org/display/CASC/phpCAS
- */
- /**
- * This class implements common methods for ProxiedService implementations included
- * with phpCAS.
- *
- * @class CAS_ProxiedService_Http_Abstract
- * @category Authentication
- * @package PhpCAS
- * @author Adam Franco <afranco@middlebury.edu>
- * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
- * @link https://wiki.jasig.org/display/CASC/phpCAS
- */
- abstract class CAS_ProxiedService_Http_Abstract extends
- CAS_ProxiedService_Abstract implements CAS_ProxiedService_Http
- {
- /**
- * The HTTP request mechanism talking to the target service.
- *
- * @var CAS_Request_RequestInterface $requestHandler
- */
- protected $requestHandler;
- /**
- * The storage mechanism for cookies set by the target service.
- *
- * @var CAS_CookieJar $_cookieJar
- */
- private $_cookieJar;
- /**
- * Constructor.
- *
- * @param CAS_Request_RequestInterface $requestHandler request handler object
- * @param CAS_CookieJar $cookieJar cookieJar object
- *
- * @return void
- */
- public function __construct(CAS_Request_RequestInterface $requestHandler,
- CAS_CookieJar $cookieJar
- ) {
- $this->requestHandler = $requestHandler;
- $this->_cookieJar = $cookieJar;
- }
- /**
- * The target service url.
- * @var string $_url;
- */
- private $_url;
- /**
- * Answer a service identifier (URL) for whom we should fetch a proxy ticket.
- *
- * @return string
- * @throws Exception If no service url is available.
- */
- public function getServiceUrl()
- {
- if (empty($this->_url)) {
- throw new CAS_ProxiedService_Exception(
- 'No URL set via ' . get_class($this) . '->setUrl($url).'
- );
- }
- return $this->_url;
- }
- /*********************************************************
- * Configure the Request
- *********************************************************/
- /**
- * Set the URL of the Request
- *
- * @param string $url url to set
- *
- * @return void
- * @throws CAS_OutOfSequenceException If called after the Request has been sent.
- */
- public function setUrl($url)
- {
- if ($this->hasBeenSent()) {
- throw new CAS_OutOfSequenceException(
- 'Cannot set the URL, request already sent.'
- );
- }
- if (!is_string($url)) {
- throw new CAS_InvalidArgumentException('$url must be a string.');
- }
- $this->_url = $url;
- }
- /*********************************************************
- * 2. Send the Request
- *********************************************************/
- /**
- * Perform the request.
- *
- * @return void
- * @throws CAS_OutOfSequenceException If called multiple times.
- * @throws CAS_ProxyTicketException If there is a proxy-ticket failure.
- * The code of the Exception will be one of:
- * PHPCAS_SERVICE_PT_NO_SERVER_RESPONSE
- * PHPCAS_SERVICE_PT_BAD_SERVER_RESPONSE
- * PHPCAS_SERVICE_PT_FAILURE
- * @throws CAS_ProxiedService_Exception If there is a failure sending the
- * request to the target service.
- */
- public function send()
- {
- if ($this->hasBeenSent()) {
- throw new CAS_OutOfSequenceException(
- 'Cannot send, request already sent.'
- );
- }
- phpCAS::traceBegin();
- // Get our proxy ticket and append it to our URL.
- $this->initializeProxyTicket();
- $url = $this->getServiceUrl();
- if (strstr($url, '?') === false) {
- $url = $url . '?ticket=' . $this->getProxyTicket();
- } else {
- $url = $url . '&ticket=' . $this->getProxyTicket();
- }
- try {
- $this->makeRequest($url);
- } catch (Exception $e) {
- phpCAS::traceEnd();
- throw $e;
- }
- }
- /**
- * Indicator of the number of requests (including redirects performed.
- *
- * @var int $_numRequests;
- */
- private $_numRequests = 0;
- /**
- * The response headers.
- *
- * @var array $_responseHeaders;
- */
- private $_responseHeaders = array();
- /**
- * The response status code.
- *
- * @var int $_responseStatusCode;
- */
- private $_responseStatusCode = '';
- /**
- * The response headers.
- *
- * @var string $_responseBody;
- */
- private $_responseBody = '';
- /**
- * Build and perform a request, following redirects
- *
- * @param string $url url for the request
- *
- * @return void
- * @throws CAS_ProxyTicketException If there is a proxy-ticket failure.
- * The code of the Exception will be one of:
- * PHPCAS_SERVICE_PT_NO_SERVER_RESPONSE
- * PHPCAS_SERVICE_PT_BAD_SERVER_RESPONSE
- * PHPCAS_SERVICE_PT_FAILURE
- * @throws CAS_ProxiedService_Exception If there is a failure sending the
- * request to the target service.
- */
- protected function makeRequest($url)
- {
- // Verify that we are not in a redirect loop
- $this->_numRequests++;
- if ($this->_numRequests > 4) {
- $message = 'Exceeded the maximum number of redirects (3) in proxied service request.';
- phpCAS::trace($message);
- throw new CAS_ProxiedService_Exception($message);
- }
- // Create a new request.
- $request = clone $this->requestHandler;
- $request->setUrl($url);
- // Add any cookies to the request.
- $request->addCookies($this->_cookieJar->getCookies($url));
- // Add any other parts of the request needed by concrete classes
- $this->populateRequest($request);
- // Perform the request.
- phpCAS::trace('Performing proxied service request to \'' . $url . '\'');
- if (!$request->send()) {
- $message = 'Could not perform proxied service request to URL`'
- . $url . '\'. ' . $request->getErrorMessage();
- phpCAS::trace($message);
- throw new CAS_ProxiedService_Exception($message);
- }
- // Store any cookies from the response;
- $this->_cookieJar->storeCookies($url, $request->getResponseHeaders());
- // Follow any redirects
- if ($redirectUrl = $this->getRedirectUrl($request->getResponseHeaders())
- ) {
- phpCAS::trace('Found redirect:' . $redirectUrl);
- $this->makeRequest($redirectUrl);
- } else {
- $this->_responseHeaders = $request->getResponseHeaders();
- $this->_responseBody = $request->getResponseBody();
- $this->_responseStatusCode = $request->getResponseStatusCode();
- }
- }
- /**
- * Add any other parts of the request needed by concrete classes
- *
- * @param CAS_Request_RequestInterface $request request interface object
- *
- * @return void
- */
- abstract protected function populateRequest(
- CAS_Request_RequestInterface $request
- );
- /**
- * Answer a redirect URL if a redirect header is found, otherwise null.
- *
- * @param array $responseHeaders response header to extract a redirect from
- *
- * @return string|null
- */
- protected function getRedirectUrl(array $responseHeaders)
- {
- // Check for the redirect after authentication
- foreach ($responseHeaders as $header) {
- if ( preg_match('/^(Location:|URI:)\s*([^\s]+.*)$/', $header, $matches)
- ) {
- return trim(array_pop($matches));
- }
- }
- return null;
- }
- /*********************************************************
- * 3. Access the response
- *********************************************************/
- /**
- * Answer true if our request has been sent yet.
- *
- * @return bool
- */
- protected function hasBeenSent()
- {
- return ($this->_numRequests > 0);
- }
- /**
- * Answer the headers of the response.
- *
- * @return array An array of header strings.
- * @throws CAS_OutOfSequenceException If called before the Request has been sent.
- */
- public function getResponseHeaders()
- {
- if (!$this->hasBeenSent()) {
- throw new CAS_OutOfSequenceException(
- 'Cannot access response, request not sent yet.'
- );
- }
- return $this->_responseHeaders;
- }
- /**
- * Answer HTTP status code of the response
- *
- * @return int
- * @throws CAS_OutOfSequenceException If called before the Request has been sent.
- */
- public function getResponseStatusCode()
- {
- if (!$this->hasBeenSent()) {
- throw new CAS_OutOfSequenceException(
- 'Cannot access response, request not sent yet.'
- );
- }
- return $this->_responseStatusCode;
- }
- /**
- * Answer the body of response.
- *
- * @return string
- * @throws CAS_OutOfSequenceException If called before the Request has been sent.
- */
- public function getResponseBody()
- {
- if (!$this->hasBeenSent()) {
- throw new CAS_OutOfSequenceException(
- 'Cannot access response, request not sent yet.'
- );
- }
- return $this->_responseBody;
- }
- /**
- * Answer the cookies from the response. This may include cookies set during
- * redirect responses.
- *
- * @return array An array containing cookies. E.g. array('name' => 'val');
- */
- public function getCookies()
- {
- return $this->_cookieJar->getCookies($this->getServiceUrl());
- }
- }
- ?>
|