diff --git a/docs/index.txt b/docs/index.txt index 355ed5ede472884f8025196d40043e971302d8bd..3ce5c8de1c534a4fa2b2bd08ac8e0297f4f0c60f 100644 --- a/docs/index.txt +++ b/docs/index.txt @@ -35,6 +35,7 @@ SimpleSAMLphp Documentation * [Key rollover](./saml:keyrollover) * [Creating authentication sources](./simplesamlphp-authsource) * [Implementing custom username/password authentication](./simplesamlphp-customauth) + * [Storing sessions in Riak](./riak:simplesamlphp-riak) Documentation on specific simpleSAMLphp modules: diff --git a/modules/riak/config-templates/module_riak.php b/modules/riak/config-templates/module_riak.php new file mode 100644 index 0000000000000000000000000000000000000000..604bb3c916fef4f2d33e0483152a86194ee28d28 --- /dev/null +++ b/modules/riak/config-templates/module_riak.php @@ -0,0 +1,17 @@ +<?php +/* + * The configuration of the riak Store module + * + * $Id$ + */ + +$config = array ( + /* + * This module has the following config options and defaults. + * + * 'path' => 'riak-php-client/riak.php', + * 'host' => 'localhost', + * 'port' => 8098, + * 'bucket' => 'SimpleSAMLphp', + */ +); diff --git a/modules/riak/default-disable b/modules/riak/default-disable new file mode 100644 index 0000000000000000000000000000000000000000..fa0bd82e2df7bd79d57593d35bc53c1f9d3ef71f --- /dev/null +++ b/modules/riak/default-disable @@ -0,0 +1,3 @@ +This file indicates that the default state of this module +is disabled. To enable, create a file named enable in the +same directory as this file. diff --git a/modules/riak/docs/simplesamlphp-riak.txt b/modules/riak/docs/simplesamlphp-riak.txt new file mode 100644 index 0000000000000000000000000000000000000000..69cf89a90e35bfc36cd9b55fc58b4a471e940486 --- /dev/null +++ b/modules/riak/docs/simplesamlphp-riak.txt @@ -0,0 +1,118 @@ +Riak Store module +================= + +<!-- + This file is written in Markdown syntax. + For more information about how to use the Markdown syntax, read here: + http://daringfireball.net/projects/markdown/syntax +--> + + * Version: `$Id$` + +<!-- {{TOC}} --> + +Introduction +------------ + +The riak module implements a Store that can be used as a backend +for simpleSAMLphp session data like the phpsession, sql, or memcache +backends. + +Preparations +------------ + +The obvious first step for using Riak as a backend is to install +and configure a Riak cluster for SimpleSAMLphp to use. Please refer +to the Riak documentation for this. + +This module requires the use of a Riak backend that supports secondary +indexes. Refer to the Riak documentation on how to enable an +appropriate backend for use by this module. Currently the only +storage backend that supports secondary indexes is leveldb. + +Next, you will need to install the Riak PHP Client library, available +from https://github.com/basho/riak-php-client. + +Finally, you need to config SimpleSAMLphp to for the riak Store by +enabling the following modules: + + 1. cron + 2. riak + +The cron module allows you to do tasks regularly by setting up a +cronjob that calls hooks in simpleSAMLphp. This is required by the +riak module to remove expired entries in the store. + +Enabling the riak module allows it to be loaded and used as a storage +backend. + +You also need to copy the `config-templates` files from the cron +module above into the global `config/` directory. + + $ cd /var/simplesamlphp + $ touch modules/cron/enable + $ cp modules/cron/config-templates/*.php config/ + $ touch modules/riak/enable + $ cp modules/riak/config-templates/*.php config/ + + +Configuring the cron module +--------------------------- + +At `/var/simplesamlphp/config` + + $ vi module_cron.php + +edit: + + $config = array ( + 'key' => 'secret', + 'allowed_tags' => array('daily', 'hourly', 'frequent'), + 'debug_message' => TRUE, + 'sendemail' => TRUE, + ); + +Then: With your browser go to => https://simplesamlphp_machine/simplesaml/module.php/cron/croninfo.php + +And copy the cron's sugestion: + + ------------------------------------------------------------------------------------------------------------------- + Cron is a way to run things regularly on unix systems. + + Here is a suggestion for a crontab file: + + # Run cron [daily] + 02 0 * * * curl --silent "https://simplesamlphp_machine/simplesaml/module.php/cron/cron.php?key=secret&tag=daily" > /dev/null 2>&1 + # Run cron [hourly] + 01 * * * * curl --silent "https://simplesamlphp_machine/simplesaml/module.php/cron/cron.php?key=secret&tag=hourly" > /dev/null 2>&1 + # Run cron [frequent] + XXXXXXXXXX curl --silent "https://simplesamlphp_machine/simplesaml/module.php/cron/cron.php?key=secret&tag=frequent" > /dev/null 2>&1 + Click here to run the cron jobs: + + Run cron [daily] + Run cron [hourly] + Run cron [frequent] + ------------------------------------------------------------------------------------------------------------------- + +Add to CRON with + + # crontab -e + +Configuring the riak module +--------------------------- + +The riak module uses the following configuration options specified +in `config/module_riak.php`. The defaults are listed: + + $config = array( + 'path' => 'riak-php-client/riak.php', + 'host' => 'localhost', + 'port' => 8098, + 'bucket' => 'SimpleSAMLphp', + ); + +Finally, the module can be specified as the Store in `config/config.php` +with the following setting: + + 'store.type' => 'riak:Store', + diff --git a/modules/riak/hooks/hook_cron.php b/modules/riak/hooks/hook_cron.php new file mode 100644 index 0000000000000000000000000000000000000000..cad1c6e3d5dd1d8e86d2a6e7fd5dfc65ec6dc295 --- /dev/null +++ b/modules/riak/hooks/hook_cron.php @@ -0,0 +1,53 @@ +<?php + +/* + * Copyright (c) 2012 The University of Queensland + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Written by David Gwynne <dlg@uq.edu.au> as part of the IT + * Infrastructure Group in the Faculty of Engineering, Architecture + * and Information Technology. + */ + + +/** + * Hook to run a cron job. + * + * @param array &$croninfo Output + */ +function riak_hook_cron(&$croninfo) { + assert('is_array($croninfo)'); + assert('array_key_exists("summary", $croninfo)'); + assert('array_key_exists("tag", $croninfo)'); + + if ($croninfo['tag'] !== 'hourly') return; + + try { + $store = new sspmod_riak_Store_Store(); + $result = $store->bucket->indexSearch('expires', 'int', + 1, time() - 30); + foreach ($result as $link) { + $link->getBinary()->delete(); + } + + SimpleSAML_Logger::info(sprintf("deleted %s riak key%s", + sizeof($result), sizeof($result) == 1 ? '' : 's')); + } catch (Exception $e) { + $message = 'riak threw exception: ' . $e->getMessage(); + SimpleSAML_Logger::warning($message); + $croninfo['summary'][] = $message; + } +} diff --git a/modules/riak/lib/Store/Store.php b/modules/riak/lib/Store/Store.php new file mode 100644 index 0000000000000000000000000000000000000000..2127ffc50a8e34fbc3c210631f6872d1fea8fc07 --- /dev/null +++ b/modules/riak/lib/Store/Store.php @@ -0,0 +1,103 @@ +<?php + +/* + * Copyright (c) 2012 The University of Queensland + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Written by David Gwynne <dlg@uq.edu.au> as part of the IT + * Infrastructure Group in the Faculty of Engineering, Architecture + * and Information Technology. + */ + +class sspmod_riak_Store_Store extends SimpleSAML_Store { + protected function __construct() { + $config = SimpleSAML_Configuration::getConfig('module_riak.php'); + + $path = $config->getString('path', 'riak-php-client/riak.php'); + $host = $config->getString('host', 'localhost'); + $port = $config->getString('port', 8098); + $bucket = $config->getString('bucket', 'simpleSAMLphp'); + + require_once($path); + $this->client = new RiakClient($host, $port); + $this->bucket = $this->client->bucket($bucket); + } + + /** + * Retrieve a value from the datastore. + * + * @param string $type The datatype. + * @param string $key The key. + * @return mixed|NULL The value. + */ + public function get($type, $key) { + assert('is_string($type)'); + assert('is_string($key)'); + + $v = $this->bucket->getBinary("$type.$key"); + if (!$v->exists()) { + return (NULL); + } + + $expires = $v->getIndex('Expires', 'int'); + if (sizeof($expires) && (int)array_shift($expires) <= time()) { + $v->delete(); + return (NULL); + } + + return (unserialize($v->getData())); + } + + + /** + * Save a value to the datastore. + * + * @param string $type The datatype. + * @param string $key The key. + * @param mixed $value The value. + * @param int|NULL $expire The expiration time (unix timestamp), or NULL if it never expires. + */ + public function set($type, $key, $value, $expire = NULL) { + assert('is_string($type)'); + assert('is_string($key)'); + assert('is_null($expire) || (is_int($expire) && $expire > 2592000)'); + + $v = $this->bucket->newBinary("$type.$key", serialize($value), 'application/php'); + if (!is_null($expire)) { + $v->addIndex("Expires", "int", $expire); + } + + $v->store(); + } + + /** + * Delete a value from the datastore. + * + * @param string $type The datatype. + * @param string $key The key. + */ + public function delete($type, $key) { + assert('is_string($type)'); + assert('is_string($key)'); + + $v = $this->bucket->getBinary("$type.$key"); + if (!$v->exists()) { + return; + } + + $v->delete(); + } +}