* @license PHP License * @package WB * @subpackage content */ WBClass::load( 'WBLog' ); /** * WBUser_Storage * * Base class use data storage * * @version 1.0.0 * @package WB * @subpackage content */ abstract class WBUser_Storage extends WBStdClass { /** * id of loaded user, if any * @var string */ protected $id = null; /** * user data of loaded user * @var array */ protected $data = array(); /** * user's group list * @var array */ protected $group = array(); /** * logger * @var WBLog */ protected $log; /** * constructor * */ public function __construct() { $this->log = WBLog::start(__CLASS__); } /** * Get Hash for Password * * Hash password for storage and tests. The hashing algorithm and parameters * configured for each user in primary user data and for new passwords * in WBParam (wb/user/password). * * This methods runs in two modes: 1) save and 2) check * * 1) Check mode when save array is empty * In this case, load hashing parameters from user data: algorithm, salt and number of iterations. * * 2) Save mode, when save array if filled * In this case, the actual hashing parameters are loaded from config in WBParam and * salt will be a random string. * * In any case, there is a backward compatibilty identified by algorithm name "md5-wombat1". * This algorithm utilizes the pretty unsecure md5 method with a simple salt using user's * id as salt. Recent installations should sha256, which is considered secure. * * All in all, this allows to upgrade md5-hashes to the new algorithm, whenever a user * changes her password. Simply set WBParam wb/user/password/algo to sha256. Make sure * your user-storage table is up to date! * * @see md5() * @see hash_pbkdf2() * @param string $password users secret password * @param array * @return string hashed string */ public function getPasswordHash($password, &$save = array()) { $algo = WBParam::get('wb/user/password/algo', 'md5-wombat1'); $iter = WBParam::get('wb/user/password/iteration', 100000); $salt = ''; // get hash for current password if (empty($save)) { $algo = $this->data['passwordalgo']; $iter = $this->data['passworditeration']; $salt = base64_decode($this->data['passwordsalt']); } else { $salt = openssl_random_pseudo_bytes(24); } if (empty($algo)) { $algo = 'md5-wombat1'; } if (empty($iter)) { $iter = 100000; } // deprecated algorithm - without further information to be saved in database if ('md5-wombat1' == $algo) { if (!empty($save) && !empty($this->data['passwordalgo'])) { $save['passwordalgo'] = $algo; $save['passworditeration'] = 1; $save['passwordsalt'] = $this->id; } return md5($this->id . ':' . $password); } $hash = hash_pbkdf2($algo, $password, $salt, $iter, 128); if (!empty($save)) { $save['passwordalgo'] = $algo; $save['passworditeration'] = $iter; $save['passwordsalt'] = base64_encode($salt); } return $hash; } /** * find user * * Allow to find user by id, nickname or email address * * @see isAuthenticated() * @param array anything find user * @return string|null either the user id on success, or null */ abstract public function find($data); /** * Unload current user data * * * @see login() * @return bool - always true */ public function clear() { if (!$this->id) { return true; } $log = array( 'action' => 'clear', 'id' => $this->id ); // log out $this->id = null; $this->data = null; $this->group = null; $this->log->notice($log); return true; } /** * load user and automatically log in * * This is like the "su" command * * @param string $id user's id * @return true on success, false otherwise */ abstract public function load($id); /** * Load Current User Data by RAW data * * @param array */ protected function loadByRaw($data) { // concat email address $data['email'] = $data['emaillocal'] . '@' . $data['emaildomain']; $this->id = $data['id']; $this->data = $data; // backward compatible if (empty($this->data['passwordalgo'])) { $this->data['passwordalgo'] = ''; $this->data['passwordsalt'] = $this->id; $this->data['passworditeration'] = 1; } } /** * Populate object with preloaded user data * * @param string $id * @param array $data * @param array $group */ public function populate($id, $data, $group) { $this->id = $id; $this->data = $data; $this->group = $group; } /** * fetch user id, if loaded * * @return string */ public function getId() { return $this->id; } /** * receive current user's data * * Get user's primary data or null if not found * * @param string $id optional user id * @return array|null */ public function get($id = null) { if ($id && !$this->load($id)) { return null; } return $this->data; } /** * get group list * * Get groups user is member of * * @param $id * @return array */ public function getGroup($id = null) { if ($id && !$this->load($id)) { return null; } if (is_array($this->group)) { $this->loadGroup(); } return $this->group; } /** * load group data * */ protected function loadGroup() { $this->group = array(); } /** * update user data * * store changed user data in session and table * * @param array $data * @return string $id on success, null otherwise */ abstract public function set($data, $new = false); /** * save group membership * * @param array $groupIds */ abstract public function setGroups($groupIds); /** * Get Members of Groups * * Load user ids by group id(s) * * @param string|array $gid * @return array list of user ids */ abstract public function getIdByGroupId($gid); }