Browse Source

Merge pull request #217 from willtp87/7.x_authentication_tokens

Token authentication.
pull/218/merge
Jonathan Green 12 years ago
parent
commit
e3ba5d3075
  1. 112
      includes/islandora_authtokens.inc
  2. 78
      islandora.install
  3. 62
      islandora.module

112
includes/islandora_authtokens.inc

@ -0,0 +1,112 @@
<?php
/**
* @file
* Library functions for handling authentication tokens.
* These are to be used when dealing with applications
* such as Djatoka that do not pass through credentials.
*/
// Token lifespan(seconds): after this duration the token expires.
// 5 minutes.
define('TOKEN_TIMEOUT', 300);
/**
* Request Islandora to construct an object/datastream authentication token.
* This token can later be turned in for access to the requested object or
* datastream.
*
* @param string $pid
* The Fedora PID to generate the token for.
* @param string $dsid
* The Fedora datastream ID to generate the token for.
* @param integer $uses
* Defaults to 1.
* The number of uses the token should be used for. There are
* times when this should be greater than 1: ie. Djatoka needs
* to make two calls.
*
* @return string
* The generated authentication token.
*/
function islandora_get_object_token($pid, $dsid, $uses = 1) {
global $user;
$time = time();
// The function mt_rand is not considered cryptographically secure
// and openssl_rando_pseudo_bytes() is only available in PHP > 5.3.
// We might be safe in this case because mt_rand should never be using
// the same seed, but this is still more secure.
$token = hash("sha256", mt_rand() . $time);
$id = db_insert("islandora_authtokens")->fields(
array(
'token' => $token,
'uid' => $user->uid,
'pid' => $pid,
'dsid' => $dsid,
'time' => $time,
'remaining_uses' => $uses,
))->execute();
return $token;
}
/**
* Submit a token to islandora for authentication. Supply islandora with the
* token and the object/datastream it is for and you will receive access if
* authentication passes. Tokens can only be redeemed in a short window after
* their creation.
*
* @param string $pid
* The PID of the object to retrieve.
* @param string $dsid
* The datastream id to retrieve.
* @param string $token
* The registered token that allows access to this object.
*
* @return mixed
* The user credentials for access if the token validation passes,
* FALSE otherwise
*/
function islandora_validate_object_token($pid, $dsid, $token) {
// Check for database token.
$time = time();
$query = db_select('islandora_authtokens', 'tokens');
$query->join('users', 'u', 'tokens.uid = u.uid');
// The results will look like user objects.
$result = $query
->fields('u', array('uid', 'name', 'pass'))
->fields('tokens', array('remaining_uses'))
->condition('token', $token, '=')
->condition('pid', $pid, '=')
->condition('dsid', $dsid, '=')
->condition('time', $time, '<=')
->condition('time', $time - TOKEN_TIMEOUT, '>')
->execute()
->fetchAll();
if ($result) {
$remaining_uses = $result[0]->remaining_uses;
$remaining_uses--;
// Remove the authentication token so it can't be used again.
if ($remaining_uses == 0) {
db_delete("islandora_authtokens")
->condition('token', $token, '=')
->condition('pid', $pid, '=')
->condition('dsid', $dsid, '=')
->execute();
}
// Decrement authentication token uses.
else {
db_update("islandora_authtokens")
->fields(array('remaining_uses' => $remaining_uses))
->condition('token', $token, '=')
->condition('pid', $pid, '=')
->condition('dsid', $dsid, '=')
->execute();
}
unset($result[0]->remaining_uses);
return $result[0];
}
else {
return FALSE;
}
}

78
islandora.install

@ -1,15 +1,13 @@
<?php
/**
* @file
*
* This file contains all install related hooks.
*/
/**
* Implements hook_install().
*
* @see islandora_islandora_required_objects().
* @see islandora_islandora_required_objects()
*/
function islandora_install() {
module_load_include('inc', 'islandora', 'includes/solution_packs');
@ -19,9 +17,81 @@ function islandora_install() {
/**
* Implements hook_uninstall().
*
* @see islandora_islandora_required_objects().
* @see islandora_islandora_required_objects()
*/
function islandora_uninstall() {
module_load_include('inc', 'islandora', 'includes/solution_packs');
islandora_install_solution_pack('islandora', 'uninstall');
}
/**
* Implements hook_schema().
*
* @return array
* The database schema for the module.
*/
function islandora_schema() {
$schema['islandora_authtokens'] = array(
'description' => 'The hub for all islandora authentication tokens',
'fields' => array(
'id' => array(
'description' => 'key',
'type' => 'serial',
'unsigned' => TRUE,
'not null' => TRUE,
),
'token' => array(
'description' => 'a unique identifier for this token',
'type' => 'varchar',
'length' => 64,
),
'remaining_uses' => array(
'description' => 'How many uses until this should be removed',
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
),
'uid' => array(
'description' => 'the user id that requested this token',
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
),
'time' => array(
'description' => 'when this token was created',
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
),
'pid' => array(
'description' => 'the pid of the object this token unlocks',
'type' => 'varchar',
'length' => 128,
'not null' => TRUE,
),
'dsid' => array(
'description' => 'the datasteram id of the object this token unlocks',
'type' => 'varchar',
'length' => 32,
'not null' => TRUE,
),
),
'unique keys' => array(
'id' => array('id'),
),
'primary key' => array('id'),
);
return $schema;
}
/**
* Implements hook_update_N().
*
* Add the required table for handling authentication tokens.
* This is the first instance that has this table.
*/
function islandora_update_7001(&$sandbox) {
drupal_install_schema('islandora');
$t = get_t();
return $t("Islandora database updates complete");
}

62
islandora.module

@ -161,10 +161,13 @@ function islandora_menu() {
'access arguments' => array(FEDORA_VIEW, 2, 4),
'load arguments' => array(2),
);
$items['islandora/object/%islandora_object/datastream/%islandora_datastream/view'] = array(
// This menu item uses token authentication in islandora_tokened_object.
$items['islandora/object/%islandora_tokened_object/datastream/%islandora_tokened_datastream/view'] = array(
'title' => 'View datastream',
'load arguments' => array('%map'),
'type' => MENU_DEFAULT_LOCAL_TASK,
);
$items['islandora/object/%islandora_object/datastream/%islandora_datastream/download'] = array(
'title' => 'Download datastream',
'page callback' => 'islandora_download_datastream',
@ -518,6 +521,55 @@ function islandora_object_load($object_id) {
return NULL;
}
/**
* A helper function to get a connection and return an object using a token
* for authentication.
*
* @param string $object_id
* The PID of an object in the menu path identified by
* '%islandora_tokened_object'.
* @param array $map
* Used to extract the Fedora object's DSID at $map[4].
*
* @return FedoraObject
* A token authenticated object. @see islandora_object_load
*/
function islandora_tokened_object_load($object_id, $map) {
if (array_key_exists('token', $_GET)) {
$token = filter_input(INPUT_GET, 'token', FILTER_SANITIZE_STRING);
if ($token) {
module_load_include('inc', 'islandora', 'includes/islandora_authtokens');
$token_user = islandora_validate_object_token($object_id, $map[4], $token);
islandora_get_tuque_connection($user = $token_user);
}
}
return islandora_object_load($object_id);
}
/**
* This datastream load must take in arguments in a different
* order than the usual islandora_datastream_load. This is because
* the function islandora_tokened_object_load needs DSID. It uses
* the path %map to avoid duplicate parameters. The menu system
* passes 'load arguments' to both islandora_tokened_object_load
* and this function and the first parameter is positional with the token.
* An alternative:
* islandora_tokened_object_load(PID, DSID, PID)
* islandora_tokened_datastream_load(DSID, DSID, PID)
*
* @param mixed $datastream_id
* %islandora_tokened_datastream @see islandora_datastream_load
* @param array $map
* Used to extract the Fedora object's PID at $map[2].
*
* @return FedoraDatastream
* A datastream from Fedora.
* @see islandora_datastream_load
*/
function islandora_tokened_datastream_load($datastream_id, $map) {
return islandora_datastream_load($datastream_id, $map[2]);
}
/**
* A helper function to get an datastream specified as '%islandora_datastream'
* for the object specified in the menu path as '%islandora_object'.
@ -526,11 +578,13 @@ function islandora_object_load($object_id) {
* drupal_access_denied() when appropriate.
*
* @param string $datastream_id
* The dsid of the datastream specified as '%islandora_datastream' to fetch
* The DSID of the datastream specified as '%islandora_datastream' to fetch
* from the given object in the menu path identified by '%islandora_object'.
*
* $param string $object_id
* The object to load the datastream from.
* @param mixed $object_id
* The object to load the datastream from. This can be a Fedora PID or
* an instantiated IslandoraFedoraObject as it implements __toString()
* returning the PID.
*
* @return FedoraDatastream
* If the given datastream ID exists then this returns a FedoraDatastream

Loading…
Cancel
Save