Community Documentation

Creeper documentation and code example : AWS tools and Drupal interactions made easy

Last updated February 9, 2012. Created by muldos on May 11, 2009.
Edited by xjm, add1sun. Log in to edit this page.

The creeper module offers a wrapper to the tarzan API, so let's start by a code example :

We will code a module that plug the drupal authentication system on an Amazon SimpleDB domain.

A few consideration before we start :
You have installed the creeper module, and set up the AWS keys in the administration > Site configuration > Tarzan settings.
We will named the module simpledbusers.

Now let's implements a very simple external authentication module based on amazon simpleDB

We begin by the .info file.

here is the simpledbusers.info file :

name = simpleDB Users module
description = Module for using Amazon Simple DB as storage for users data
core = 6.x
dependencies[] = creeper

The important thing here is the line

dependencies[] = creeper

Which will allow you to use easily the tarzan API in this module code.

This module is so simple that it needn't a .install file, so move on studying the .module file :

<?php


//////////////////////////////// DRUPAL HOOKS  IMPLEMENTATION ///////////////////////////

/**
* Implementation of hook_menu()
* a simple admin settings screen which will
* allow to set up the salt for encoding users passwords
*/
function simpledbusers_menu() {
 
$items = array();

 
$items['admin/settings/simpledbusers/settings'] = array(
   
'title' => 'Simple DB users Settings',
   
'page callback' => 'drupal_get_form',
   
'page arguments' => array('simpledbusers_admin_settings'),
   
'description' => t('Configure Simple DB Users settings'),
   
'access arguments' => array('administer site configuration'),
   
'file' => 'simpledbusers.admin.inc'
   
);
  return
$items;
}


/**
* implements hook_form_alter()
* in order to change the validator of the default login form
*/

function simpledbusers_form_alter(&$form, $form_state, $form_id) {
 
// Replace the drupal authenticate function is it's used as validation.
 
if($form_id == 'user_login' || $form_id == 'user_login_block'){
    if (
is_array($form['#validate']) && ($key = array_search('user_login_authenticate_validate', $form['#validate']))){
     
$form['#validate'][$key] = 'simpledbusers_login_validate';
    }
    else if (
$key === FALSE) {
     
// Could not find it. Some other module must have run form_alter().
      // We will simply add our validation just before the final validator.
     
$final_validator = array_pop($form['#validate']);
     
$form['#validate'][] = 'simpledbusers_login_validate';
     
$form['#validate'][] = $final_validator;
    }

  }
}

/**
* Main user validation function.
*
* If successful, sets the global $user object.
*
*
*/

function simpledbusers_login_validate($form, &$form_state) {

  global
$user;
  if (!empty(
$user->uid)) {
   
// Another module has already handled authentication.
   
return;
  }

 
$values = $form_state['values'];
 
$pass = trim($values['pass']);

 
// (Design decision) uid=1 (admin user) must always authenticate to local database
  // this user is critical for all drupal admin and upgrade operations so it is best
  // left with drupal's native authentication.
 
$result = db_query("SELECT uid FROM {users} WHERE name = '%s'", $values['name']);
 
$account = db_fetch_object($result);
  if (
$account->uid == 1 ) {
   
$account = user_load(array('name' => $values['name'], 'pass' => trim($values['pass']), 'status' => 1));
   
$user = $account;
   
//drupal way to handle properly an external authentication
   
user_authenticate_finalize($values);
    return;
  }
 
 
// Authenticate on simpledb the user.
 
if(!simpledbusers_auth($values['name'], $pass)) {
   
form_set_error('name',t('Unrecognized username/password'));
  }
  return;
}


/**
* Implements hook_user().
* mainly to allow synchro between the drupal user ref and the simpleDB user ref.
*/
function simpledbusers_user($op, &$edit, &$account, $category = NULL) {
  switch (
$op) {
  
  
//submit op allow to modify the account before it gets saved
   //so each time the account is modified we sync with the SimpleDB domain
  
case 'submit':
   if(
$account->uid != 1){
     
$account->pass=$edit['pass'];
     
simpledbusers_update_account_infos($account,$edit);
     }
   break;
   
//the delete op is called After the delete op in the database
   
case 'delete' :
      if(!
$registration_delete){
        
simpledbusers_user_delete($account);
       }
      break;

    case
'insert':
     
// New user was just added; if we authenticate the user via simpledb, we sync the user's data
      // because the user has been auto created in drupal db if we are here.
      // we  set the uid generated by drupal in the users domain and the email field from simpledb
     
global $simpledbusers_authenticated;
      if (
$simpledbusers_authenticated) {
        
simpledbusers_create_content_profile($account);
        
//add the validated user role
        
$roles = $account->roles + array(6 => 'validated user');
        
user_save($account, array('roles' => $roles));
        
simpledbusers_update_uid($account);

      }
      else {
//creation of user by registration or admin creation
       
         
$account->pass = $edit['pass']; // we need the clear password
           //we set a false password in database for the user
         
db_query("UPDATE {users} SET pass = '%s' WHERE uid = %d", user_password(), $account->uid);
       
//we create the simpledb item
         
simpledbusers_register_user(array('account' => $account));
      
      }
      break;
  }
}


//////////////////// SIMPLE DB INTERACTIONS FUNCTIONS //////////////////////////

/**
* Authenticate the user against SimpleDB.
*
* @param $name
*   A username.
* @param $pass
*   A password.
*
* @return
*  TRUE if success, FALSE otherwise.
*/
function simpledbusers_auth($name, $pass) {
  global
$simpledbusers_authenticated;
 
// Don't allow empty passwords because they cause problems on some setups.
 
if (empty($pass))
  return
FALSE;


 
$sha265_password = simpledbusers_encode_password($pass);
 
// Try to authenticate.
 
$sdb = new AmazonSDB();
 
$selection = $sdb->select("select name from users where name = '".$name."' and password= '".$sha265_password."'");
    
  if(!
$selection->isOK()) {
   
form_set_error('name',t('Problem during authentication request'));
    return
FALSE;
  }
  if(!isset(
$selection->body->SelectResult->Item))
    return
FALSE;
 
 
//authentication ok :
 
$simpledbusers_authenticated = TRUE;
 
user_external_login_register($name, 'simpledbusers');
  return
TRUE;
}

/*
* Delete a user item in the simpledb users domain.
*/
function simpledbusers_user_delete($account) {

 
$item_name = $account->name;

 
$sdb = new AmazonSDB();
 
 
$del = $sdb->delete_attributes(variable_get('simpledbusers_simpledb_domain','users'),$item_name);
//todo add error handling here....
}



/*
* Update in simpledb the uid user attribute
*
*/

function simpledbusers_update_uid($account)
{
 
$sdb = new AmazonSDB();

$keypairs = array(
   
'drupal_uid' => $account->uid,
  );
 
$put = $sdb->put_attributes(variable_get('simpledbusers_simpledb_domain','users'),$account->name,$keypairs,TRUE);
}

/*
* Update simpledb mail and password user attribute
*
*/

function simpledbusers_update_account_infos($account,$edit)
{ global
$external_account_update_ok;
 
$external_account_update_ok = TRUE;
 
$sdb = new AmazonSDB();
  if(isset(
$edit['mail'])){
 
$keypairs = array('email' => $edit['mail']);
    if(isset(
$edit['pass'])){
       
$keypairs = $keypairs + array('password' => simpledbusers_encode_password($edit['pass']));
    }
   
$put = $sdb->put_attributes(variable_get('simpledbusers_simpledb_domain','users'),$account->name,$keypairs,TRUE);
   
$external_account_update_ok = $put->isOK();
  }
}
/*
* Create an item in the simpledb users domain for a new user
*/
function simpledbusers_register_user($args) {

 
$account = $args['account'];

 
//the password is availaible in clear text at the moment (cf the hook_user code)
 
$account_clear_pwd = $account->pass;
 
$account = user_save($account,  array('authname_simpledbusers' => $account->name));

 
$sdb = new AmazonSDB();

 
$keypairs = array(
   
'name' => $account->name,
   
'email' => $account->mail,
   
'password' => simpledbusers_encode_password($account_clear_pwd),
   
//sample additional field
   
'time_purchased' => time() + (6 * 30 * 24 * 60 * 60),
  );
 
 
$put = $sdb->put_attributes(variable_get('simpledbusers_simpledb_domain','users'),$account->name,$keypairs);
  global
$external_registration_ok;//global var that other modules ca use to check elsewhere to see if anything strange occured
 
if(!$put->isOK())
    
$external_registration_ok = FALSE;//a problem occur if we are here so we set the flag
}


function
simpledbusers_encode_password($clear_password) {
  return
hash('sha256',$clear_password.variable_get('simpledbusers_salt','mys4ltH3r3'));
}


?>

The important things here are the simpleDB interactions functions, with for example :

<?php
function simpledbusers_register_user($args) {

 
$account = $args['account'];

 
//the password is availaible in clear text at the moment (cf the hook_user code)
 
$account_clear_pwd = $account->pass;
 
$account = user_save($account,  array('authname_simpledbusers' => $account->name));

 
$sdb = new AmazonSDB();

 
$keypairs = array(
   
'name' => $account->name,
   
'email' => $account->mail,
   
'password' => simpledbusers_encode_password($account_clear_pwd),
   
//sample additional field
   
'time_purchased' => time() + (6 * 30 * 24 * 60 * 60),
  );
 
 
$put = $sdb->put_attributes(variable_get('simpledbusers_simpledb_domain','users'),$account->name,$keypairs);
  global
$external_registration_ok;//global var that other modules ca use to check elsewhere to see if anything strange occured 
 
if(!$put->isOK())
    
$external_registration_ok = FALSE;//a problem occur so set the flag
}
?>

Or

<?php
/*
* Delete a user item in the simpledb users domain.
*/
function simpledbusers_user_delete($account) {

 
$item_name = $account->name;

 
$sdb = new AmazonSDB();
 
 
$del = $sdb->delete_attributes(variable_get('simpledbusers_simpledb_domain','users'),$item_name);
//todo add error handling here, depending on your needs....
}
?>

You can notice the use of the AmazonSDB class, the entrance point to the SimpleDB API of the tarzan framework :

$sdb = new AmazonSDB();

Simple isn't it ?

the simpleDB domain (a domain is mainly an SQL table equivalent in Amazon simpleDB) used here include a name, a password a drupal_uid field and an additional "time_purchased" field as example.

for the records here is the simpledbusers.admin.inc but it's not the point to focus on for this example :

<?php
function simpledbusers_admin_settings(&$form_state) {

 
$form = array();
 
$form['simpledb-settings'] = array(
   
'#type' => 'fieldset',
   
'#title' => t('Simple DB settings'),
   
'#collapsible' => TRUE,
   
'#collapsed' => FALSE,
  );

 
$form['simpledb-settings']['simpledbusers_simpledb_domain'] = array(
   
'#type' => 'textfield',
   
'#title' => t('Domain Name'),
   
'#default_value' =>  variable_get('simpledbusers_simpledb_domain','users'),
   
'#size' => 50,
   
'#maxlength' => 255,
   
'#description' => t('<p>The SimpleDB domain name where users will be stored.</p>'),
   
'#required' => TRUE,
  );

$form['simpledb-settings']['simpledbusers_salt'] = array(
   
'#type' => 'textfield',
   
'#title' => t('Password Salt'),
   
'#default_value' =>  variable_get('simpledbusers_salt','mys4ltH3r3'),
   
'#size' => 50,
   
'#maxlength' => 255,
   
'#description' => t('<p>The salt used for passwords encryption.</p>'),
   
'#required' => TRUE,
  );

  return
system_settings_form($form);
}

function
simpledbusers_admin_settings_submit($form,&$form_state) {

 
variable_set('simpledbusers_simpledb_domain',$form_state['values']['simpledbusers_simpledb_domain']);
 
variable_set('simpledbusers_salt',$form_state['values']['simpledbusers_salt']);
}
?>

Conclusion
This simple example demonstrate that when you declare a dependencie to the creeper module, all you need to worry to interact with AWS is the Cloudfusion API : http://getcloudfusion.com/docs
I have found drupal dev with cloudfusion very pleasant because you use 2 good APIs and quickly make powerfull things easily.

About this page

Drupal version
Drupal 6.x
Audience
Programmers

Site Building Guide

Drupal’s online documentation is © 2000-2013 by the individual contributors and can be used in accordance with the Creative Commons License, Attribution-ShareAlike 2.0. PHP code is distributed under the GNU General Public License. Comments on documentation pages are used to improve content and then deleted.