Squiz Matrix  4.12.2
 All Data Structures Namespaces Functions Variables Pages
saml2_acs.inc
1 <?php
21 require_once SQ_INCLUDE_PATH.'/asset.inc';
22 
36 class Saml2_ACS extends Asset
37 {
38 
39 
46  function __construct($assetid=0)
47  {
48  parent::__construct($assetid);
49 
50  }//end constructor
51 
52 
62  function _getName($short_name=FALSE)
63  {
64  return $this->attr('name');
65 
66  }//end _getName()
67 
68 
69 
80  protected function _createAdditional(Array &$link)
81  {
82  if (!parent::_createAdditional($link)) return FALSE;
83 
84  // NOTE: the main time we wont want to do this is if we are creating
85  // the root folder as it is not linked anywhere
86  if (empty($link)) return TRUE;
87 
88  return $this->makeAndSaveInitialWebPath(strtolower($this->attr('name')), $link);
89 
90  }//end _createAdditional()
91 
92 
99  function printBody()
100  {
101  // get the source id from parent
102  $saml_account_manager_data = $GLOBALS['SQ_SYSTEM']->am->getLink($this->id, SQ_LINK_TYPE_2, 'saml_account_manager', TRUE, 'saml2_assertion_consumer_service', 'minor');
103  $saml_account_manager = $GLOBALS['SQ_SYSTEM']->am->getAsset($saml_account_manager_data['majorid']);
104 
105  if(!$saml_account_manager->isSimplesamlphpInstalled()) {
106  echo('simplesamlphp library is not installed');
107  return FALSE;
108  }
109 
110  require_once SQ_TOOL_SIMPLESAMLPHP_PATH.'/lib/_autoload.php';
111  require_once SQ_PACKAGES_PATH.'/saml/lib/saml_sp.inc';
112 
113 
114  $sourceId = $saml_account_manager->attr('auth_source');
115 
116  $config = SimpleSAML_Configuration::getConfig('authsources.php');
117  $authConfig = $config->getArray($sourceId, NULL);
118  $source = new SAML_Service_Provider (Array('AuthId' => $sourceId), $authConfig);
119 
120  $spMetadata = $source->getMetadata();
121  try {
122  $b = SAML2_Binding::getCurrentBinding();
123  }
124  catch (Exception $e) {
125  echo($e->getMessage());
126  return FALSE;
127  }
128 
129  if ($b instanceof SAML2_HTTPArtifact) {
130  $b->setSPMetadata($spMetadata);
131  }
132 
133  $response = $b->receive();
134 
135  if (!($response instanceof SAML2_Response)) {
136  throw new SimpleSAML_Error_BadRequest('Invalid message received to AssertionConsumerService endpoint.');
137  }
138 
139  $idp = $response->getIssuer();
140  if ($idp === NULL) {
141  /* No Issuer in the response. Look for an unencrypted assertion with an issuer. */
142  foreach ($response->getAssertions() as $a) {
143  if ($a instanceof SAML2_Assertion) {
144  /* We found an unencrypted assertion - there should be an issuer here. */
145  $idp = $a->getIssuer();
146  break;
147  }
148  }
149  if ($idp === NULL) {
150  /* No issuer found in the assertions. */
151  throw new Exception('Missing <saml:Issuer> in message delivered to AssertionConsumerService.');
152  }
153  }
154 
155  $session = SimpleSAML_Session::getInstance();
156  $prevAuth = $session->getAuthData($sourceId, 'saml:sp:prevAuth');
157  if ($prevAuth !== NULL && $prevAuth['id'] === $response->getId() && $prevAuth['issuer'] === $idp) {
158  /* OK, it looks like this message has the same issuer
159  * and ID as the SP session we already have active. We
160  * therefore assume that the user has somehow triggered
161  * a resend of the message.
162  * In that case we may as well just redo the previous redirect
163  * instead of displaying a confusing error message.
164  */
165  SimpleSAML_Logger::info('Duplicate SAML 2 response detected - ignoring the response and redirecting the user to the correct page.');
166  SimpleSAML_Utilities::redirect($prevAuth['redirect']);
167  }
168 
169  $stateId = $response->getInResponseTo();
170  if (!empty($stateId)) {
171  /* This is a response to a request we sent earlier. */
172  $state = SimpleSAML_Auth_State::loadState($stateId, 'saml:sp:sso');
173 
174  /* Check that the authentication source is correct. */
175  assert('array_key_exists("saml:sp:AuthId", $state)');
176  if ($state['saml:sp:AuthId'] !== $sourceId) {
177  throw new SimpleSAML_Error_Exception('The authentication source id in the URL does not match the authentication source which sent the request.');
178  }
179  } else {
180  /* This is an unsolicited response. */
181  $state = array(
182  'saml:sp:isUnsolicited' => TRUE,
183  'saml:sp:AuthId' => $sourceId,
184  'saml:sp:RelayState' => $response->getRelayState(),
185  );
186  }
187 
188  SimpleSAML_Logger::debug('Received SAML2 Response from ' . var_export($idp, TRUE) . '.');
189 
190  $idpMetadata = $source->getIdPmetadata($idp);
191 
192  try {
193  $assertions = sspmod_saml_Message::processResponse($spMetadata, $idpMetadata, $response);
194  } catch (sspmod_saml_Error $e) {
195  /* The status of the response wasn't "success".
196  * Redirect back to saml account manager page and display error bodycopy
197  */
198  $_SESSION['sq_saml_account_manager_auth_error'] = $e->getStatusMessage();
199  $return = $state['SimpleSAML_Auth_Default.Return'];
200  if (is_string($return)) {
201  /* Redirect... */
202  SimpleSAML_Utilities::redirect($return);
203  }
204  assert('FALSE');
205 
206  }
207 
208 
209  $authenticatingAuthority = NULL;
210  $nameId = NULL;
211  $sessionIndex = NULL;
212  $expire = NULL;
213  $attributes = array();
214  $foundAuthnStatement = FALSE;
215  foreach ($assertions as $assertion) {
216 
217  /* Check for duplicate assertion (replay attack). */
218  $store = SimpleSAML_Store::getInstance();
219  if ($store !== FALSE) {
220  $aID = $assertion->getId();
221  if ($store->get('saml.AssertionReceived', $aID) !== NULL) {
222  $e = new SimpleSAML_Error_Exception('Received duplicate assertion.');
223  SimpleSAML_Auth_State::throwException($state, $e);
224  }
225 
226  $notOnOrAfter = $assertion->getNotOnOrAfter();
227  if ($notOnOrAfter === NULL) {
228  $notOnOrAfter = time() + 24*60*60;
229  } else {
230  $notOnOrAfter += 60; /* We allow 60 seconds clock skew, so add it here also. */
231  }
232 
233  $store->set('saml.AssertionReceived', $aID, TRUE, $notOnOrAfter);
234  }
235 
236 
237  if ($authenticatingAuthority === NULL) {
238  $authenticatingAuthority = $assertion->getAuthenticatingAuthority();
239  }
240  if ($nameId === NULL) {
241  $nameId = $assertion->getNameId();
242  }
243  if ($sessionIndex === NULL) {
244  $sessionIndex = $assertion->getSessionIndex();
245  }
246  if ($expire === NULL) {
247  $expire = $assertion->getSessionNotOnOrAfter();
248  }
249 
250  $attributes = array_merge($attributes, $assertion->getAttributes());
251 
252  if ($assertion->getAuthnInstant() !== NULL) {
253  /* Assertion contains AuthnStatement, since AuthnInstant is a required attribute. */
254  $foundAuthnStatement = TRUE;
255  }
256  }
257 
258  if (!$foundAuthnStatement) {
259  $e = new SimpleSAML_Error_Exception('No AuthnStatement found in assertion(s).');
260  SimpleSAML_Auth_State::throwException($state, $e);
261  }
262 
263  if ($expire === NULL) {
264  /* Just expire the logout associtaion 24 hours into the future. */
265  $expire = time() + 24*60*60;
266  }
267 
268  /* Register this session in the logout store. */
269  sspmod_saml_SP_LogoutStore::addSession($sourceId, $nameId, $sessionIndex, $expire);
270 
271  /* We need to save the NameID and SessionIndex for logout. */
272  $logoutState = array(
273  'saml:logout:Type' => 'saml2',
274  'saml:logout:IdP' => $idp,
275  'saml:logout:NameID' => $nameId,
276  'saml:logout:SessionIndex' => $sessionIndex,
277  );
278  $state['LogoutState'] = $logoutState;
279  $state['saml:AuthenticatingAuthority'] = $authenticatingAuthority;
280  $state['saml:AuthenticatingAuthority'][] = $idp;
281  $state['PersistentAuthData'][] = 'saml:AuthenticatingAuthority';
282 
283  $state['saml:sp:IdP'] = $idp;
284  $state['PersistentAuthData'][] = 'saml:sp:IdP';
285  $state['saml:sp:NameID'] = $nameId;
286  $state['PersistentAuthData'][] = 'saml:sp:NameID';
287  $state['saml:sp:SessionIndex'] = $sessionIndex;
288  $state['PersistentAuthData'][] = 'saml:sp:SessionIndex';
289  $state['saml:sp:AuthnContext'] = $assertion->getAuthnContext();
290  $state['PersistentAuthData'][] = 'saml:sp:AuthnContext';
291 
292 
293  if (isset($state['SimpleSAML_Auth_Default.ReturnURL'])) {
294  /* Just note some information about the authentication, in case we receive the
295  * same response again.
296  */
297  $state['saml:sp:prevAuth'] = array(
298  'id' => $response->getId(),
299  'issuer' => $idp,
300  'redirect' => $state['SimpleSAML_Auth_Default.ReturnURL'],
301  );
302  $state['PersistentAuthData'][] = 'saml:sp:prevAuth';
303  }
304 
305  $source->handleResponse($state, $idp, $attributes);
306  assert('FALSE');
307 
308  }
309 
310 }//end class
311 ?>