Ejemplo n.º 1
0
 /**
  * Proxy authenticates to a target service.
  *
  * Returns cookies from the proxied service in a
  * CookieJar object for use when later accessing resources.
  *
  * @param string $target_service
  *   The service to be proxied.
  *
  * @return \GuzzleHttp\Cookie\CookieJar
  *   A CookieJar object (array storage) containing cookies from the
  *   proxied service.
  *
  * @throws CasProxyException
  */
 public function proxyAuthenticate($target_service)
 {
     // Check to see if we have proxied this application already.
     if (isset($_SESSION['cas_proxy_helper'][$target_service])) {
         $cookies = array();
         foreach ($_SESSION['cas_proxy_helper'][$target_service] as $cookie) {
             $cookies[$cookie['Name']] = $cookie['Value'];
         }
         $domain = $cookie['Domain'];
         $jar = CookieJar::fromArray($cookies, $domain);
         $this->casHelper->log("{$target_service} already proxied. Returning information from session.");
         return $jar;
     }
     if (!($this->casHelper->isProxy() && isset($_SESSION['cas_pgt']))) {
         // We can't perform proxy authentication in this state.
         throw new CasProxyException("Session state not sufficient for proxying.");
     }
     // Make request to CAS server to retrieve a proxy ticket for this service.
     $cas_url = $this->getServerProxyURL($target_service);
     try {
         $this->casHelper->log("Retrieving proxy ticket from: {$cas_url}");
         $response = $this->httpClient->get($cas_url);
         $this->casHelper->log("Received: " . htmlspecialchars($response->getBody()->__toString()));
     } catch (ClientException $e) {
         throw new CasProxyException($e->getMessage());
     }
     $proxy_ticket = $this->parseProxyTicket($response->getBody());
     $this->casHelper->log("Extracted proxy ticket: {$proxy_ticket}");
     // Make request to target service with our new proxy ticket.
     // The target service will validate this ticket against the CAS server
     // and set a cookie that grants authentication for further resource calls.
     $params['ticket'] = $proxy_ticket;
     $service_url = $target_service . "?" . UrlHelper::buildQuery($params);
     $cookie_jar = new CookieJar();
     try {
         $this->casHelper->log("Contacting service: {$service_url}");
         $this->httpClient->get($service_url, ['cookies' => $cookie_jar]);
     } catch (ClientException $e) {
         throw new CasProxyException($e->getMessage());
     }
     // Store in session storage for later reuse.
     $_SESSION['cas_proxy_helper'][$target_service] = $cookie_jar->toArray();
     $this->casHelper->log("Stored cookies from {$target_service} in session.");
     return $cookie_jar;
 }
Ejemplo n.º 2
0
 /**
  * Handles a request to either validate a user login or log a user out.
  *
  * The path that this controller/action handle are always set to the "service"
  * when authenticating with the CAS server, so CAS server communicates back to
  * the Drupal site using this controller.
  */
 public function handle()
 {
     $request = $this->requestStack->getCurrentRequest();
     // First, check if this is a single-log-out (SLO) request from the server.
     if ($request->request->has('logoutRequest')) {
         $this->casHelper->log("Logout request: passing to casLogout::handleSlo");
         $this->casLogout->handleSlo($request->request->get('logoutRequest'));
         // Always return a 200 code. CAS Server doesn’t care either way what
         // happens here, since it is a fire-and-forget approach taken.
         return Response::create('', 200);
     }
     // Our CAS Subscriber, which implements forced redirect and gateway, will
     // set this query string param which indicates we should disable the
     // subscriber on the next redirect. This prevents an infinite redirect loop.
     if ($request->query->has('cas_temp_disable')) {
         $this->casHelper->log("Temp disable flag set, set session flag.");
         $_SESSION['cas_temp_disable'] = TRUE;
     }
     // Check if there is a ticket parameter. If there isn't, we could be
     // returning from a gateway request and the user may not be logged into CAS.
     // Just redirect away from here.
     if (!$request->query->has('ticket')) {
         $this->casHelper->log("No ticket detected, move along.");
         $this->handleReturnToParameter($request);
         return RedirectResponse::create($this->urlGenerator->generate('<front>'));
     }
     $ticket = $request->query->get('ticket');
     // Our CAS service will need to reconstruct the original service URL
     // when validating the ticket. We always know what the base URL for
     // the service URL (it's this page), but there may be some query params
     // attached as well (like a destination param) that we need to pass in
     // as well. So, detach the ticket param, and pass the rest off.
     $service_params = $request->query->all();
     unset($service_params['ticket']);
     $cas_version = $this->casHelper->getCasProtocolVersion();
     $this->casHelper->log("Configured to use CAS protocol version: {$cas_version}");
     try {
         $cas_validation_info = $this->casValidator->validateTicket($cas_version, $ticket, $service_params);
     } catch (CasValidateException $e) {
         // Validation failed, redirect to homepage and set message.
         $this->setMessage(t('There was a problem validating your login, please contact a site administrator.'), 'error');
         $this->handleReturnToParameter($request);
         return RedirectResponse::create($this->urlGenerator->generate('<front>'));
     }
     try {
         $this->casLogin->loginToDrupal($cas_validation_info, $ticket);
         if ($this->casHelper->isProxy() && $cas_validation_info->getPgt()) {
             $this->casHelper->log("Storing PGT information for this session.");
             $this->casHelper->storePGTSession($cas_validation_info->getPgt());
         }
         $this->setMessage(t('You have been logged in.'));
     } catch (CasLoginException $e) {
         $this->setMessage(t('There was a problem logging in, please contact a site administrator.'), 'error');
     }
     $this->handleReturnToParameter($request);
     return RedirectResponse::create($this->urlGenerator->generate('<front>'));
 }
Ejemplo n.º 3
0
 /**
  * Validation of a service ticket for Version 2 of the CAS protocol.
  *
  * @param string $data
  *   The raw validation response data from CAS server.
  *
  * @return array
  *   An array containing validation result data from the CAS server.
  * @throws CasValidateException
  */
 private function validateVersion2($data)
 {
     $dom = new \DOMDocument();
     $dom->preserveWhiteSpace = FALSE;
     $dom->encoding = "utf-8";
     // Suppress errors from this function, as we intend to throw our own
     // exception.
     if (@$dom->loadXML($data) === FALSE) {
         throw new CasValidateException("XML from CAS server is not valid.");
     }
     $failure_elements = $dom->getElementsByTagName('authenticationFailure');
     if ($failure_elements->length > 0) {
         // Failed validation, extract the message and toss exception.
         $failure_element = $failure_elements->item(0);
         $error_code = $failure_element->getAttribute('code');
         $error_msg = $failure_element->nodeValue;
         throw new CasValidateException("Error Code " . trim($error_code) . ": " . trim($error_msg));
     }
     $success_elements = $dom->getElementsByTagName("authenticationSuccess");
     if ($success_elements->length === 0) {
         // All responses should have either an authenticationFailure
         // or authenticationSuccess node.
         throw new CasValidateException("XML from CAS server is not valid.");
     }
     // There should only be one success element, grab it and extract username.
     $success_element = $success_elements->item(0);
     $user_element = $success_element->getElementsByTagName("user");
     if ($user_element->length == 0) {
         throw new CasValidateException("No user found in ticket validation response.");
     }
     $username = $user_element->item(0)->nodeValue;
     $this->casHelper->log("Extracted user: {$username}");
     $property_bag = new CasPropertyBag($username);
     // If the server provided any attributes, parse them out into the property
     // bag.
     $attribute_elements = $dom->getElementsByTagName("attributes");
     if ($attribute_elements->length > 0) {
         $property_bag->setAttributes($this->parseAttributes($attribute_elements));
     }
     // Look for a proxy chain, and if it exists, validate it against config.
     $proxy_chain = $success_element->getElementsByTagName("proxy");
     if ($this->casHelper->canBeProxied() && $proxy_chain->length > 0) {
         $this->verifyProxyChain($proxy_chain);
     }
     if ($this->casHelper->isProxy()) {
         // Extract the PGTIOU from the XML.
         $pgt_element = $success_element->getElementsByTagName("proxyGrantingTicket");
         if ($pgt_element->length == 0) {
             throw new CasValidateException("Proxy initialized, but no PGTIOU provided in response.");
         }
         $pgt = $pgt_element->item(0)->nodeValue;
         $this->casHelper->log("Extracted PGT: {$pgt}");
         $property_bag->setPgt($pgt);
     }
     return $property_bag;
 }
Ejemplo n.º 4
0
 /**
  * Main point of communication between CAS server and the Drupal site.
  *
  * The path that this controller/action handle are always set to the "service"
  * url when authenticating with the CAS server, so CAS server communicates
  * back to the Drupal site using this controller action. That's why there's
  * so much going on in here - it needs to process a few different types of
  * requests.
  */
 public function handle()
 {
     $request = $this->requestStack->getCurrentRequest();
     // First, check if this is a single-log-out (SLO) request from the server.
     if ($request->request->has('logoutRequest')) {
         try {
             $this->casLogout->handleSlo($request->request->get('logoutRequest'));
         } catch (CasSloException $e) {
             $this->casHelper->log($e->getMessage());
         }
         // Always return a 200 code. CAS Server doesn’t care either way what
         // happens here, since it is a fire-and-forget approach taken.
         return Response::create('', 200);
     }
     // We will be redirecting the user below. To prevent the CasSubscriber from
     // initiating an automatic authentiation on the that request (like forced
     // auth or gateway auth) and potentially creating an authentication loop,
     // we set a session variable instructing the CasSubscriber skip auto auth
     // for that request.
     $request->getSession()->set('cas_temp_disable_auto_auth', TRUE);
     /* If there is no ticket parameter on the request, the browser either:
      * (a) is returning from a gateway request to the CAS server in which
      *     the user was not already authenticated to CAS, so there is no
      *     service ticket to validate and nothing to do.
      * (b) has hit this URL for some other reason (crawler, curiosity, etc)
      *     and there is nothing to do.
      * In either case, we just want to redirect them away from this controller.
      */
     if (!$request->query->has('ticket')) {
         $this->casHelper->log("No ticket detected, move along.");
         $this->handleReturnToParameter($request);
         return RedirectResponse::create($this->urlGenerator->generate('<front>'));
     }
     // There is a ticket present, meaning CAS server has returned the browser
     // to the Drupal site so we can authenticate the user locally using the
     // ticket.
     $ticket = $request->query->get('ticket');
     // Our CAS service will need to reconstruct the original service URL
     // when validating the ticket. We always know what the base URL for
     // the service URL (it's this page), but there may be some query params
     // attached as well (like a destination param) that we need to pass in
     // as well. So, detach the ticket param, and pass the rest off.
     $service_params = $request->query->all();
     unset($service_params['ticket']);
     try {
         $cas_validation_info = $this->casValidator->validateTicket($ticket, $service_params);
     } catch (CasValidateException $e) {
         // Validation failed, redirect to homepage and set message.
         $this->casHelper->log($e->getMessage());
         $this->setMessage($this->t('There was a problem validating your login, please contact a site administrator.'), 'error');
         $this->handleReturnToParameter($request);
         return RedirectResponse::create($this->urlGenerator->generate('<front>'));
     }
     // Now that the ticket has been validated, we can use the information from
     // validation request to authenticate the user locally on the Drupal site.
     try {
         $this->casLogin->loginToDrupal($cas_validation_info, $ticket);
         if ($this->casHelper->isProxy() && $cas_validation_info->getPgt()) {
             $this->casHelper->log("Storing PGT information for this session.");
             $this->casHelper->storePgtSession($cas_validation_info->getPgt());
         }
         $this->setMessage($this->t('You have been logged in.'));
     } catch (CasLoginException $e) {
         $this->casHelper->log($e->getMessage());
         $this->setMessage($this->t('There was a problem logging in, please contact a site administrator.'), 'error');
     }
     // And finally redirect the user to the homepage, or so a specific
     // destination found in the destination param (like the page they were on
     // prior to initiating authentication).
     $this->handleReturnToParameter($request);
     return RedirectResponse::create($this->urlGenerator->generate('<front>'));
 }
Ejemplo n.º 5
0
 /**
  * Test getting the 'act as proxy' configuration.
  *
  * @covers ::isProxy
  * @covers ::__construct
  */
 public function testIsProxy()
 {
     $config_factory = $this->getConfigFactoryStub(array('cas.settings' => array('server.hostname' => 'example.com', 'server.port' => 443, 'server.path' => '/cas', 'proxy.initialize' => TRUE)));
     $cas_helper = new CasHelper($config_factory, $this->urlGenerator, $this->connection, $this->loggerFactory, $this->session);
     $this->assertEquals(TRUE, $cas_helper->isProxy());
 }