class Bakery in Bakery Single Sign-On System 7.3
Hierarchy
- class \Bakery
Expanded class hierarchy of Bakery
3 string references to 'Bakery'
- Bakery::debug in ./
bakery.inc - Bakery::log in ./
bakery.inc - bakery_menu in ./
bakery.module - Implements hook_menu().
File
- ./
bakery.inc, line 3
View source
class Bakery {
// If Bakery is being used as the account master.
protected $isMaster;
// Cookie domain.
protected $domain;
// Secret key shared between Bakery sites for encryption/decryption.
protected $key;
// Name of cookie used for SSO.
protected $SsoCookieName;
// Name of cookie used for login and registration redirection.
protected $subCookieName;
// Cookie data lifetime in seconds.
protected $lifetime;
// Cookie expiration time in seconds.
protected $expiration;
// Array of Bakery sub-sites. FQDN and ending in a forward slash (/).
//protected $slaves; // @todo not used
// Optional debug messaging. Drupal dependant.
protected $debug;
// Holds an instance of the Bakery object.
protected static $instance = NULL;
/**
*
* @param array Array of Bakery settings. Required keys:
* 'is_master' (bool) Whether this site is a Bakery master or not.
* 'domain' (string) Bakery domain.
* 'key' (string) Per-cluster Bakery secret key.
*
*/
public function __construct(array $config) {
$this->isMaster = $config['is_master'];
$this->domain = $config['domain'];
$this->key = $config['key'];
$this->SsoCookieName = isset($config['sso_cookie']) ? $config['sso_cookie'] : 'BAKERYSSO';
//$this->loginCookieName = isset($options['login_cookie']) ? $options['login_cookie'] : 'BAKERYLOG';
$this->subCookieName = isset($config['sub_cookie']) ? $config['sub_cookie'] : 'BAKERYSUB';
$this->lifetime = isset($config['lifetime']) ? $config['lifetime'] : 3600;
$this->expiration = isset($config['expiration']) ? $config['expiration'] : 3600 * 24 * 7;
//$this->slaves = isset($config['slaves']) ? $config['slaves'] : array();
$this->debug = isset($config['debug']) ? $config['debug'] : FALSE;
}
/**
* Returns an instance of the Bakery object.
*
* @param array Array of Bakery settings. Required keys:
* 'is_master' bool Whether this site is a Bakery master or not.
* 'domain' string Bakery domain.
* 'key' string Per-cluster Bakery secret key.
*
* @return Bakery object.
*/
public static function instance(array $options = array()) {
if (!self::$instance) {
self::$instance = new self($options);
}
return self::$instance;
}
/**
* Create SSO cookie for account.
*
* @param array $params Array of account parameters. Must contain.
* 'name'
* 'mail'
*/
public function setSsoCookie($params) {
$cookie = array();
$type = $this->SsoCookieName;
$cookie['type'] = $type;
$cookie['name'] = $params['name'];
$cookie['mail'] = $params['mail'];
$cookie['init'] = $params['init'];
$cookie['uid'] = $params['uid'];
$cookie['data'] = isset($params['data']) ? $params['data'] : array();
$cookie['master'] = $this->isMaster;
$cookie['timestamp'] = $_SERVER['REQUEST_TIME'];
$data = $this
->bakeData($cookie);
$this
->setCookie($type, $data);
}
/**
* Create cookie used during sub-site login.
*
* @param string Account name
* @param array Data to store in cookie.
* @param string URL of site starting redirection.
* FQDN and must end in forward slash (/).
*/
/*public function setLoginCookie($name, $data, $start_url) {
$cookie = array();
$type = $this->loginCookieName;
$cookie['type'] = $type;
$cookie['name'] = $account->name;
$cookie['data'] = $data;
$cookie['master'] = $this->isMaster;
$cookie['slave'] = $start_url;
$cookie['timestamp'] = $_SERVER['REQUEST_TIME'];
$data = $this->bakeData($cookie);
$this->setCookie($type, $data);
}*/
/**
* Create cookie used during sub-site registration.
*
* @param string Account name
* @param array Data to store in cookie.
* @param string URL of site starting redirection.
* FQDN and must end in forward slash (/).
*/
public function setSubCookie($name, $data, $start_url) {
$cookie = array();
$type = $this->subCookieName;
$cookie['type'] = $type;
$cookie['name'] = $name;
$cookie['data'] = $data;
$cookie['master'] = $this->isMaster;
$cookie['slave'] = $start_url;
$cookie['timestamp'] = $_SERVER['REQUEST_TIME'];
$data = $this
->bakeData($cookie);
$this
->setCookie($type, $data);
}
/**
* Check and validate account SSO cookie for request.
*
* @return mixed FALSE if cookie is not valid, NULL if not set, or array if
* valid cookie.
*/
public function validateSsoCookie() {
$type = $this->SsoCookieName;
if (!isset($_COOKIE[$type]) || !$this->key || !$this->domain) {
// No cookie is set or site is misconfigured. Return NULL so existing
// cookie is not deleted.
return NULL;
}
try {
$data = $this
->validateData($_COOKIE[$type], $type);
$this
->debug('in validate SSO ', $data);
return $data;
} catch (BakeryException $e) {
$this
->log('Validation exception:', $e
->getMessage());
return FALSE;
}
}
/**
* Check and validate cookie used in login or registration from sub-site.
*
* @return mixed FALSE if cookie is not valid, NULL if not set, or array if
* valid cookie.
*/
public function validateSubCookie() {
$type = $this->subCookieName;
if (!isset($_COOKIE[$type]) || !$this->key || !$this->domain) {
// No cookie is set or site is misconfigured. Return NULL so existing
// cookie is not deleted.
return NULL;
}
try {
$data = $this
->validateData($_COOKIE[$type], $type);
$this
->debug('in validate Sub', $data);
return $data;
} catch (BakeryException $e) {
$this
->log('Validation exception:', $e
->getMessage());
return FALSE;
}
}
/**
* Delete SSO cookie.
*/
public function deleteSsoCookie() {
$this
->deleteCookie($this->SsoCookieName);
}
/**
* Delete sub-site cookie.
*/
public function deleteSubCookie() {
$this
->deleteCookie($this->subCookieName);
}
/**
* Set cookie.
*
* @param string $name Cookie name.
* @param string $data Cookie data.
*/
protected function setCookie($name, $data) {
$cookie_secure = ini_get('session.cookie_secure');
setcookie($name, $data, $_SERVER['REQUEST_TIME'] + $this->expiration, '/', $this->domain, empty($cookie_secure) ? FALSE : TRUE);
}
/**
* Delete cookie by setting empty value.
*
* @param string $name Cookie name.
*/
protected function deleteCookie($name) {
$cookie_secure = ini_get('session.cookie_secure');
setcookie($name, '', $_SERVER['REQUEST_TIME'] - 3600, '/', '', empty($cookie_secure) ? FALSE : TRUE);
setcookie($name, '', $_SERVER['REQUEST_TIME'] - 3600, '/', $this->domain, empty($cookie_secure) ? FALSE : TRUE);
}
/**
* Serialze, sign and encode data for secure transport.
*
* @param array $data Raw data to encrypt.
* @return string Base 64 encoded signed and encrypted data.
*/
protected function bakeData(array $data) {
$this
->debug('bake ' . $data['type'], $data);
$data = $this
->serialize($data);
$encrypted_data = $this
->encrypt($data);
$signature = $this
->sign($encrypted_data);
return base64_encode($signature . $encrypted_data);
}
/**
* Validate and decrypt baked data.
*
* @param string $data Baked data.
* @param string $type Cookie type.
* @return array Original, raw data.
*/
protected function validateData($data, $type) {
$this
->debug('validated data', $data);
$data = base64_decode($data);
$signature = substr($data, 0, 64);
$encrypted_data = substr($data, 64);
if ($signature !== $this
->sign($encrypted_data)) {
throw new BakeryException(3001, 'Signature mismatch');
}
$data = $this
->decrypt($encrypted_data);
$decrypted_data = $this
->unserialize($data);
$this
->debug('decrypted', $decrypted_data);
// Prevent one cookie being used in place of another.
if ($type !== NULL && $decrypted_data['type'] !== $type) {
throw new BakeryException(3002, 'Type mismatch');
}
if ($decrypted_data['timestamp'] + $this->lifetime >= $_SERVER['REQUEST_TIME']) {
return $decrypted_data;
}
else {
throw new BakeryException(3003, 'Data expired');
}
}
/**
* Encrypt text.
*
* @param string $text Unencrypted base64 encoded text.
* @return string Encrypted text.
*/
private function encrypt($text) {
$key = $this->key;
$td = mcrypt_module_open('rijndael-128', '', 'ecb', '');
$iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
$key = substr($key, 0, mcrypt_enc_get_key_size($td));
mcrypt_generic_init($td, $key, $iv);
$data = mcrypt_generic($td, $text);
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
return $data;
}
/**
* Decrypt text.
*
* @param string $data Serialized plain text to encrypt.
* @return string Decrypted text.
*/
private function decrypt($data) {
$key = $this->key;
$td = mcrypt_module_open('rijndael-128', '', 'ecb', '');
$iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
$key = substr($key, 0, mcrypt_enc_get_key_size($td));
mcrypt_generic_init($td, $key, $iv);
$text = mdecrypt_generic($td, $data);
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
return $text;
}
/**
* Return hash of data.
*
* @param string $data Text to sign.
* @return string 64 character signature.
*/
private function sign($data) {
$key = $this->key;
return hash_hmac('sha256', $data, $key);
}
/**
* Serialize an array.
*
* @param array $data Array to be serialized.
* @return string Serialized data.
*/
protected function serialize($data) {
// @todo figure out how to get JSON encoding to work, it current decodes
// as NULL
return serialize($data);
}
/**
* Unserialize into an object or array.
*
* @param string $data Serialized data.
* @return array Unserialized data.
*/
protected function unserialize($data) {
// @todo figure out how to get JSON encoding to work, it current decodes
// as NULL
return unserialize($data);
}
private function log($message, $data = array()) {
watchdog('Bakery', '@message @data', array(
'@message' => $message,
'@data' => var_export($data, TRUE),
), WATCHDOG_NOTICE);
}
private function debug($message, $data = array()) {
if ($this->debug) {
// @todo strip out pass
watchdog('Bakery', '@message @data', array(
'@message' => $message,
'@data' => var_export($data, TRUE),
), WATCHDOG_DEBUG);
}
}
}
Members
Name | Modifiers | Type | Description | Overrides |
---|---|---|---|---|
Bakery:: |
protected | property | ||
Bakery:: |
protected | property | ||
Bakery:: |
protected | property | ||
Bakery:: |
protected static | property | ||
Bakery:: |
protected | property | ||
Bakery:: |
protected | property | ||
Bakery:: |
protected | property | ||
Bakery:: |
protected | property | ||
Bakery:: |
protected | property | ||
Bakery:: |
protected | function | Serialze, sign and encode data for secure transport. | |
Bakery:: |
private | function | ||
Bakery:: |
private | function | Decrypt text. | |
Bakery:: |
protected | function | Delete cookie by setting empty value. | |
Bakery:: |
public | function | Delete SSO cookie. | |
Bakery:: |
public | function | Delete sub-site cookie. | |
Bakery:: |
private | function | Encrypt text. | |
Bakery:: |
public static | function | Returns an instance of the Bakery object. | |
Bakery:: |
private | function | ||
Bakery:: |
protected | function | Serialize an array. | |
Bakery:: |
protected | function | Set cookie. | |
Bakery:: |
public | function | Create SSO cookie for account. | |
Bakery:: |
public | function | Create cookie used during sub-site registration. | |
Bakery:: |
private | function | Return hash of data. | |
Bakery:: |
protected | function | Unserialize into an object or array. | |
Bakery:: |
protected | function | Validate and decrypt baked data. | |
Bakery:: |
public | function | Check and validate account SSO cookie for request. | |
Bakery:: |
public | function | Check and validate cookie used in login or registration from sub-site. | |
Bakery:: |
public | function |