You are here

class Bakery in Bakery Single Sign-On System 7.3

Hierarchy

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

Namesort descending Modifiers Type Description Overrides
Bakery::$debug protected property
Bakery::$domain protected property
Bakery::$expiration protected property
Bakery::$instance protected static property
Bakery::$isMaster protected property
Bakery::$key protected property
Bakery::$lifetime protected property
Bakery::$SsoCookieName protected property
Bakery::$subCookieName protected property
Bakery::bakeData protected function Serialze, sign and encode data for secure transport.
Bakery::debug private function
Bakery::decrypt private function Decrypt text.
Bakery::deleteCookie protected function Delete cookie by setting empty value.
Bakery::deleteSsoCookie public function Delete SSO cookie.
Bakery::deleteSubCookie public function Delete sub-site cookie.
Bakery::encrypt private function Encrypt text.
Bakery::instance public static function Returns an instance of the Bakery object.
Bakery::log private function
Bakery::serialize protected function Serialize an array.
Bakery::setCookie protected function Set cookie.
Bakery::setSsoCookie public function Create SSO cookie for account.
Bakery::setSubCookie public function Create cookie used during sub-site registration.
Bakery::sign private function Return hash of data.
Bakery::unserialize protected function Unserialize into an object or array.
Bakery::validateData protected function Validate and decrypt baked data.
Bakery::validateSsoCookie public function Check and validate account SSO cookie for request.
Bakery::validateSubCookie public function Check and validate cookie used in login or registration from sub-site.
Bakery::__construct public function