Normal PHP session support

81 views
Skip to first unread message

tlbdk

unread,
May 10, 2009, 4:51:49 PM5/10/09
to Drupal SimpleSAMLphp integration
Hi, I spent a couple of hours hacking on getting PHP sessions to work
in the Drupal SimpleSAMLphp module, this should make the module a lot
easier to use as it now does not require using memcache.

As Drupal hijacks PHP session handling, the way I have done it, is by
grabbing the session file SimpleSAMLphp made when doing the
authentication, and doing the decoding without the help of PHP's
session handling code.

The code still needs to check the PHP session defaults and the
SimpleSAMLphp config for the correct paths and and variable names.
Also I don't quite know if it's SimpleSAMLphp phpSession
implementation that is broken as it assumes nobody messes with the
session handling, so maybe a better place for this code is in
SimpleSAMLphp?

Btw. I also removed all the str_replace('http://', 'https://'.. as it
makes testing a cumbersome affair and it's an assumption that does not
always hold in some vhost setups. eg. http://www.it-kartellet.dk =
https://secure.it-kartellet.dk. At least it should be optional?

But anyways, have a look at the patch and give some comments.

Regards
Troels.

diff --git a/sites/all/modules/simplesamlphp_auth/
simplesamlphp_auth.module b/sites/all/modules/simplesamlphp_auth/
simplesamlphp_auth.module
index 574f5f7..4da4c89 100644
--- a/sites/all/modules/simplesamlphp_auth/simplesamlphp_auth.module
+++ b/sites/all/modules/simplesamlphp_auth/simplesamlphp_auth.module
@@ -4,6 +4,7 @@
* simpleSAMLphp authentication module for Drupal.
*
* (c) UNINETT, Andreas Åkre Solberg, <andreas...@uninett.no>.
http://rnd.feide.no
+ * (c) IT-Kartellet, Troels Liebe Bentsen <tro...@it-kartellet.dk>.
http://www.it-kartellet.dk
*
* This authentication module is based on the shibboleth
authentication module, with changes
* to adopt to use simpleSAMLphp.
@@ -38,11 +39,26 @@ function simplesamlphp_auth_init() {
global $saml_config;

//get the simplesamlphp session
- $basedir = variable_get('simplesamlphp_auth_installdir', '/var/
simplesamlphp');
+ $basedir = variable_get('simplesamlphp_auth_installdir', '/home/tlb/
apps/simplesamlphp');
require_once($basedir . '/www/_include.php');
+
+ // TODO: use SimpleSAML configuration to find out how session is
stored, ie. cookie name etc.
+ // Drupal overrides the session handeling functions so we don't
have access
+ // to sesssion the SimpleSAML login got for us, so we have to fetch
the
+ // session directly from the directory where PHP stores them.
+
+ // Get current PHP session ID
+ $session_id = $_COOKIE['PHPSESSID'];
+ // Slurp session file into $data
+ $data = implode('', file("/var/lib/php5/sess_$session_id"));
+ // Decode that session data, we can't use sesssion_decode as this
closes the header.
+ $session_data = _session_real_decode($data);
+ // Inject session into $_SESSION so SimpleSAML_Session::getInstance
() can find it.
+ $_SESSION['SimpleSAMLphp_SESSION'] = $session_data
['SimpleSAMLphp_SESSION'];
+
/* Load simpleSAMLphp, configuration and metadata */
$saml_config = SimpleSAML_Configuration::getInstance();
- $saml_session = SimpleSAML_Session::getInstance(FALSE);
+ $saml_session = SimpleSAML_Session::getInstance();

if ($user->uid) {
//user is logged in - Drupal
@@ -97,8 +113,6 @@ function simplesamlphp_auth_init() {

}

-
-
/**
* Implementation of hook_user().
*/
@@ -191,7 +205,6 @@ function simplesamlphp_auth_settings() {

}

-
function simplesamlphp_auth_form_user_login_alter(&$form, &
$form_state) {

$form['simplesaml_login_link'] = array(
@@ -248,6 +261,7 @@ function _simplesamlphp_auth_get_authname() {
throw new Exception(t('error in
simplesamlphp_auth.module: no valid unique id attribute set'));
}
}
+
return $authname;
}

@@ -319,7 +333,7 @@ function _simplesamlphp_auth_get_logouturl() {

/* Check if valid local session exists.. */
if (isset($saml_session) && $saml_session->isValid() ) {
- $initSLOurl = str_replace('http://', 'https://',
SimpleSAML_Utilities::selfURLhost()) .
+ $initSLOurl = SimpleSAML_Utilities::selfURLhost().
'/' . $saml_config->getValue('baseurlpath') . 'saml2/sp/
initSLO.php?RelayState=' .
urlencode(variable_get('simplesamlphp_auth_logout_page','/'));
}
@@ -342,10 +356,9 @@ function _simplesamlphp_auth_generate_login_text
() {
require_once((isset($SIMPLESAML_INCPREFIX)?
$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Session.php');
require_once((isset($SIMPLESAML_INCPREFIX)?
$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/XHTML/Template.php');

- $initSSOurl = str_replace('http://', 'https://',
SimpleSAML_Utilities::selfURLhost()) .
- '/' . $saml_config->getValue('baseurlpath') . 'saml2/sp/initSSO.php?
RelayState=' .
- urlencode(str_replace('http://', 'https://',
SimpleSAML_Utilities::selfURL()));
-
+ $initSSOurl =SimpleSAML_Utilities::selfURLhost(). '/'
+ . $saml_config->getBaseURL(). 'saml2/sp/initSSO.php?RelayState='
+ . urlencode(SimpleSAML_Utilities::selfURL());

/* Check if valid local session exists.. */
if (isset($saml_session) && $saml_session->isValid() ) {
@@ -368,9 +381,9 @@ function _simplesamlphp_auth_generate_block_text()
{
require_once((isset($SIMPLESAML_INCPREFIX)?
$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Session.php');
require_once((isset($SIMPLESAML_INCPREFIX)?
$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/XHTML/Template.php');

- $initSSOurl = str_replace('http://', 'https://',
SimpleSAML_Utilities::selfURLhost()) .
- '/' . $saml_config->getValue('baseurlpath') . 'saml2/sp/initSSO.php?
RelayState=' .
- urlencode(str_replace('http://', 'https://',
SimpleSAML_Utilities::selfURL()));
+ $initSSOurl =SimpleSAML_Utilities::selfURLhost(). '/'
+ . $saml_config->getBaseURL(). 'saml2/sp/initSSO.php?
RelayState='
+ . urlencode(SimpleSAML_Utilities::selfURL());

/* Check if valid local session exists.. */
if (isset($saml_session) && $saml_session->isValid() ) {
@@ -402,7 +415,6 @@ function _simplesamlphp_auth_evaulaterolerule
($roleruleevaluation, $attributes)
return false;
}

-
function _simplesamlphp_auth_rolepopulation($rolemap) {
global $saml_session;
$roles = array();
@@ -448,7 +460,6 @@ function _simplesamlphp_auth_evaulaterolerule
($roleruleevaluation, $attributes)
}


-
/**
* This helper function is used by developers to debug the form API
workflow in this module.
* Uncomment the function body to activate.
@@ -456,3 +467,91 @@ function _simplesamlphp_auth_evaulaterolerule
($roleruleevaluation, $attributes)
function _simplesaml_auth_debug($message) {
watchdog('simplesamlphp_auth', $message, NULL, WATCHDOG_DEBUG);
}
+
+// Code from http://dk.php.net/manual/en/function.session-decode.php#56106
+function _session_real_decode($str)
+{
+ define('PS_DELIMITER', '|');
+ define('PS_UNDEF_MARKER', '!');
+ $str = (string)$str;
+
+ $endptr = strlen($str);
+ $p = 0;
+
+ $serialized = '';
+ $items = 0;
+ $level = 0;
+
+ while ($p < $endptr) {
+ $q = $p;
+ while ($str[$q] != PS_DELIMITER)
+ if (++$q >= $endptr) break 2;
+
+ if ($str[$p] == PS_UNDEF_MARKER) {
+ $p++;
+ $has_value = false;
+ } else {
+ $has_value = true;
+ }
+
+ $name = substr($str, $p, $q - $p);
+ $q++;
+
+ $serialized .= 's:' . strlen($name) . ':"' . $name . '";';
+
+ if ($has_value) {
+ for (;;) {
+ $p = $q;
+ switch (strtolower($str[$q])) {
+ case 'n': /* null */
+ case 'b': /* boolean */
+ case 'i': /* integer */
+ case 'd': /* decimal */
+ do $q++;
+ while ( ($q < $endptr) && ($str[$q] !=
';') );
+ $q++;
+ $serialized .= substr($str, $p, $q - $p);
+ if ($level == 0) break 2;
+ break;
+ case 'r': /* reference */
+ $q+= 2;
+ for ($id = ''; ($q < $endptr) && ($str[$q] !=
';'); $q++) $id .= $str[$q];
+ $q++;
+ $serialized .= 'R:' . ($id + 1) . ';'; /*
increment pointer because of outer array */
+ if ($level == 0) break 2;
+ break;
+ case 's': /* string */
+ $q+=2;
+ for ($length=''; ($q < $endptr) && ($str[$q] !
= ':'); $q++) $length .= $str[$q];
+ $q+=2;
+ $q+= (int)$length + 2;
+ $serialized .= substr($str, $p, $q - $p);
+ if ($level == 0) break 2;
+ break;
+ case 'a': /* array */
+ case 'O': /* object */
+ do $q++;
+ while ( ($q < $endptr) && ($str[$q] !=
'{') );
+ $q++;
+ $level++;
+ $serialized .= substr($str, $p, $q - $p);
+ break;
+ case '}': /* end of array|object */
+ $q++;
+ $serialized .= substr($str, $p, $q - $p);
+ if (--$level == 0) break 2;
+ break;
+ default:
+ return false;
+ }
+ }
+ } else {
+ $serialized .= 'N;';
+ $q+= 2;
+ }
+ $items++;
+ $p = $q;
+ }
+ return @unserialize( 'a:' . $items . ':{' . $serialized . '}' );
+};
+

Snorre Løvås

unread,
May 11, 2009, 1:30:08 PM5/11/09
to Drupal SimpleSAMLphp integration


On May 10, 10:51 pm, tlbdk <t...@it-kartellet.dk> wrote:

> Btw. I also removed all the str_replace('http://', 'https://'.. as it
> makes testing a cumbersome affair and it's an assumption that does not
> always hold in some vhost setups. eg.http://www.it-kartellet.dk=https://secure.it-kartellet.dk. At least it should be optional?

I thought about it when I did the rewrite, but left it. Just added a
configuration option in the trunk now.


> But anyways, have a look at the patch and give some comments.

Andreas said he'd look into the rest of it, but it might take some
time.

Regards,
Snorre

totten

unread,
May 11, 2009, 3:52:28 PM5/11/09
to Drupal SimpleSAMLphp integration
This is really interesting to me. I was in exactly the same situation
-- the memcache dependency is unworkable in my environment, so I spent
a few hours last week on doing an integration between Drupal and
SimpleSAMLphp's session handler. The approach was a little different,
so I'll describe it.

SimpleSAMLphp includes a "SessionHandler" concept which can plug in
different session handling logic -- e.g. the SessionHandlerPHP and
SessionHandlerMemcache implement different session mechanisms. I
implemented a SessionHandlerDrupal. The implementation seemed pretty
straightforward considering that Drupal and SessionHandlerPHP both use
PHP sessions. Moreover, it seems to open possibilities for using
Drupal as both (a) an SP and (b) the user-visible front-end of an IdP.
I've got the SP side working, and I'll be working on the IdP side in
the next couple days.

So far, the main hang up turned on some session handling extensions in
the two applications -- both Drupal and SimpleSAMLphp use
register_shutdown_function to add session-handling logic at the end of
the page request. Unfortunately, these two shutdown functions create a
race condition, and the resulting session data is encoded
inconsistently.

I haven't shared the solution yet because (a) it's a little rough
(requires small patches in several spots) and (b) the IdP stuff isn't
done yet. Changes are:

1. Add two config options to simpleSAMLphp's config.php

2. Add an extra line to Drupal to include the SimpleSAMLphp class
loader

3. Add a new module to SimpleSAMLphp (called "drupalsession") to
actually integrate the sessions

4. Add a few lines to SimpleSAMLphp to work-around the race condition
and add a couple hooks. (The patch aims for minimalism rather than
correctness -- a more correct patch would remove the race condition
but would require bigger changes.)

5. Some changes to the simplesamlphp_auth module (which I don't fully
remember right now).

However, I definitely want to look at the patch posted above, and I'll
try to post my version soon.

Best,
Tim

matt

unread,
Jun 2, 2009, 7:13:31 AM6/2/09
to Drupal SimpleSAMLphp integration
Sounds great and really interesting prospects for using Drupal as an
IdP.

I have this drupal module working today...
http://drupal.org/project/shib_auth
... but I've become very intrigued with the prospects for using
SimpleSAMLphp.

So far, I have it (SimpleSAMLphp) installed, but I'm having trouble
getting it to work with the TestShib IdP (http://www.testshib.org/
testshib-two/index.jsp). I'm missing something simple there, I'm
sure, but once I get it up and running, I'd be interested in the
potential for collaborating on this.

In the meantime, I'm looking forward to seeing where you go with it.

Cheers,

Matt Pasiewicz
EDUCAUSE
Reply all
Reply to author
Forward
0 new messages