/** * Attempts to log the authenticated CAS user into Drupal. * * This method should be used to login a user after they have successfully * authenticated with the CAS server. * * @param CasPropertyBag $property_bag * CasPropertyBag containing username and attributes from CAS. * @param string $ticket * The service ticket. * * @throws CasLoginException * Thrown if there was a problem logging in the user. */ public function loginToDrupal(CasPropertyBag $property_bag, $ticket) { // Dispatch an event that allows modules to change user data we received // from CAS before attempting to use it to load a Drupal user. // Auto-registration can also be disabled for this user if their account // does not exist. $user_load_event = new CasUserLoadEvent($property_bag); $this->eventDispatcher->dispatch(CasHelper::EVENT_USER_LOAD, $user_load_event); $account = $this->userLoadByName($property_bag->getUsername()); if (!$account) { $config = $this->settings->get('cas.settings'); if ($config->get('user_accounts.auto_register') === TRUE) { if ($user_load_event->allowAutoRegister) { $account = $this->registerUser($property_bag->getUsername(), $config->get('user_accounts.auto_assigned_roles')); } else { throw new CasLoginException("Cannot register user, an event listener denied access."); } } else { throw new CasLoginException("Cannot login, local Drupal user account does not exist."); } } // Dispatch an event that allows modules to prevent this user from logging // in and/or alter the user entity before we save it. $pre_auth_event = new CasPreAuthEvent($account, $property_bag); $this->eventDispatcher->dispatch(CasHelper::EVENT_PRE_AUTH, $pre_auth_event); // Save user entity since event listeners may have altered it. $account->save(); if (!$pre_auth_event->allowLogin) { throw new CasLoginException("Cannot login, an event listener denied access."); } $this->userLoginFinalize($account); $this->storeLoginSessionData($this->session->getId(), $ticket); }
/** * Test setting the attributes array. * * @covers ::setAttributes */ public function testSetAttributes() { $bag = new CasPropertyBag($this->randomMachineName(8)); $attributes = array('foo' => array('bar'), 'baz' => array('quux, foobar')); $bag->setAttributes($attributes); $this->assertEquals($attributes, \PHPUnit_Framework_Assert::readAttribute($bag, 'attributes')); }
/** * Attempts to log the authenticated CAS user into Drupal. * * This method should be used to login a user after they have successfully * authenticated with the CAS server. * * @param CasPropertyBag $property_bag * CasPropertyBag containing username and attributes from CAS. * * @throws CasLoginException */ public function loginToDrupal(CasPropertyBag $property_bag, $ticket) { $this->eventDispatcher->dispatch(CasHelper::CAS_PROPERTY_ALTER, new CasPropertyEvent($property_bag)); $account = $this->userLoadByName($property_bag->getUsername()); if (!$account) { $config = $this->settings->get('cas.settings'); if ($config->get('user_accounts.auto_register') === TRUE) { if (!$property_bag->getRegisterStatus()) { $_SESSION['cas_temp_disable'] = TRUE; throw new CasLoginException("Cannot register user, an event listener denied access."); } $account = $this->registerUser($property_bag->getUsername()); } else { throw new CasLoginException("Cannot login, local Drupal user account does not exist."); } } $this->eventDispatcher->dispatch(CasHelper::CAS_USER_ALTER, new CasUserEvent($account, $property_bag)); $account->save(); if (!$property_bag->getLoginStatus()) { $_SESSION['cas_temp_disable'] = TRUE; throw new CasLoginException("Cannot login, an event listener denied access."); } $this->userLoginFinalize($account); $this->storeLoginSessionData($this->sessionManager->getId(), $ticket); }
/** * Test parsing out CAS attributes from response. * * @covers ::validateVersion2 * @covers ::parseAttributes */ public function testParseAttributes() { $ticket = $this->randomMachineName(8); $service_params = array(); $response = "<cas:serviceResponse xmlns:cas='http://example.com/cas'>\n <cas:authenticationSuccess>\n <cas:user>username</cas:user>\n <cas:attributes>\n <cas:email>foo@example.com</cas:email>\n <cas:memberof>cn=foo,o=example</cas:memberof>\n <cas:memberof>cn=bar,o=example</cas:memberof>\n </cas:attributes>\n </cas:authenticationSuccess>\n </cas:serviceResponse>"; $mock = new MockHandler([new Response(200, array(), $response)]); $handler = HandlerStack::create($mock); $httpClient = new Client(['handler' => $handler]); $casHelper = $this->getMockBuilder('\\Drupal\\cas\\Service\\CasHelper')->disableOriginalConstructor()->getMock(); $casHelper->expects($this->any())->method('getCasProtocolVersion')->willReturn('2.0'); $casValidator = new CasValidator($httpClient, $casHelper); $expected_bag = new CasPropertyBag('username'); $expected_bag->setAttributes(array('email' => array('*****@*****.**'), 'memberof' => array('cn=foo,o=example', 'cn=bar,o=example'))); $actual_bag = $casValidator->validateTicket($ticket, $service_params); $this->assertEquals($expected_bag, $actual_bag); }
/** * Test setting the register status. * * @covers ::setRegisterStatus */ public function testSetRegisterStatus() { $bag = new CasPropertyBag($this->randomMachineName(8)); $bag->setRegisterStatus(FALSE); $this->assertEquals(FALSE, \PHPUnit_Framework_Assert::readAttribute($bag, 'registerStatus')); }
/** * Provides parameters and exceptions for testLoginToDrupalListenerDenied * * @return array * Parameters and exceptions * * @see \Drupal\Tests\cas\Unit\Service\CasLoginTest::testLoginToDrupalListenerDenied */ public function loginToDrupalListenerDeniedDataProvider() { // Test denying login access. $bag1 = new CasPropertyBag($this->randomMachineName(8)); $bag1->setLoginStatus(FALSE); $message1 = 'Cannot login, an event listener denied access.'; // Test denying register access. $bag2 = new CasPropertyBag($this->randomMachineName(8)); $bag2->setRegisterStatus(FALSE); $message2 = 'Cannot register user, an event listener denied access.'; return array(array($bag1, $message1), array($bag2, $message2)); }
/** * 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; }
/** * Asserts that validation is executed. */ private function assertSuccessfulValidation($returnto, $for_proxy = FALSE) { $service_params = array(); if ($returnto) { $service_params['returnto'] = 'node/1'; } $validation_data = new CasPropertyBag('testuser'); if ($for_proxy) { $validation_data->setPgt('testpgt'); } // Validation service should be called for that ticket. $this->casValidator->expects($this->once())->method('validateTicket')->with($this->equalTo('ST-foobar'), $this->equalTo($service_params))->will($this->returnValue($validation_data)); }