* @link http://getkirby.com * @copyright Bastian Allgeier * @license http://getkirby.com/license */ abstract class UserAbstract { protected $username = null; protected $cache = array(); protected $data = null; public function __construct($username) { $this->username = str::slug(basename($username)); // check if the account file exists if(!file_exists($this->file())) { throw new Exception('The user account could not be found'); } } /** * Returns the username * * @return string */ public function username() { return $this->username; } /** * get all data for the user */ public function data() { if(!is_null($this->data)) return $this->data; // get all data from the account file $this->data = data::read($this->file(), 'yaml'); // make sure all keys are lowercase $this->data = array_change_key_case($this->data, CASE_LOWER); // remove garbage unset($this->data[0]); // add the username $this->data['username'] = $this->username; // return the data array return $this->data; } public function __get($key) { return a::get($this->data(), strtolower($key)); } public function __call($key, $arguments = null) { return $this->__get($key); } public function role() { $roles = kirby::instance()->site()->roles(); $data = $this->data(); if(empty($data['role'])) { // apply the default role, if no role is stored for the user $data['role'] = $roles->findDefault()->id(); } // return the role by id if($role = $roles->get($data['role'])) { return $role; } else { return $roles->findDefault(); } } public function hasRole() { $roles = func_get_args(); return in_array($this->role()->id(), $roles); } // support for old 'panel' role permission public function hasPanelAccess() { return $this->role()->hasPermission('panel.access'); } public function hasPermission($target) { return $this->role()->hasPermission($target); } public function isAdmin() { return $this->role()->id() == 'admin'; } public function avatar() { if(isset($this->cache['avatar'])) return $this->cache['avatar']; $avatar = new Avatar($this); return $this->cache['avatar'] = $avatar->exists() ? $avatar : false; } public function avatarRoot($extension = 'jpg') { return kirby::instance()->roots()->avatars() . DS . $this->username() . '.' . $extension; } public function gravatar($size = 256) { return gravatar($this->email(), $size); } protected function file() { return kirby::instance()->roots()->accounts() . DS . $this->username() . '.php'; } public function textfile() { return $this->file(); } public function exists() { return file_exists($this->file()); } public function generateKey() { return str::random(64); } public function generateSecret($key) { return sha1($this->username() . $key); } public function login($password) { static::logout(); if(!password::match($password, $this->password)) return false; // create a new session id s::regenerateId(); $key = $this->generateKey(); $secret = $this->generateSecret($key); s::set('kirby_auth_secret', $secret); s::set('kirby_auth_username', $this->username()); cookie::set( s::$name . '_auth', $key, s::$cookie['lifetime'], s::$cookie['path'], s::$cookie['domain'], s::$cookie['secure'], s::$cookie['httponly'] ); return true; } static public function logout() { s::destroy(); cookie::remove(s::$name . '_auth'); } public function is($user) { if(!is_a($user, 'User')) return false; return $this->username() === $user->username(); } public function isCurrent() { return $this->is(static::current()); } static public function validate($data = array(), $mode = 'insert') { if($mode == 'insert') { if(empty($data['username'])) { throw new Exception('Invalid username'); } if(empty($data['password'])) { throw new Exception('Invalid password'); } } if(!empty($data['email']) and !v::email($data['email'])) { throw new Exception('Invalid email'); } } public function update($data = array()) { // sanitize the given data $data = $this->sanitize($data, 'update'); // validate the updated dataset $this->validate($data, 'update'); // don't update the username unset($data['username']); // create a new hash for the password if(!empty($data['password'])) { $data['password'] = password::hash($data['password']); } // merge with existing fields $this->data = array_merge($this->data(), $data); foreach($this->data as $key => $value) { if(is_null($value)) unset($this->data[$key]); } // save the new user data static::save($this->file(), $this->data); // return the updated user project return $this; } public function delete() { if($avatar = $this->avatar()) { $avatar->delete(); } if(!f::remove($this->file())) { throw new Exception('The account could not be deleted'); } else { return true; } } static public function sanitize($data, $mode = 'insert') { // all usernames must be lowercase $data['username'] = str::slug(a::get($data, 'username')); // convert all keys to lowercase $data = array_change_key_case($data, CASE_LOWER); // return the cleaned up data return $data; } /** * Creates a new user * * @param array $user * @return User */ static public function create($data = array()) { // sanitize the given data for the new user $data = static::sanitize($data, 'insert'); // validate the dataset static::validate($data, 'insert'); // create the file root $file = kirby::instance()->roots()->accounts() . DS . $data['username'] . '.php'; // check for an existing username if(file_exists($file)) { throw new Exception('The username is taken'); } // create a new hash for the password if(!empty($data['password'])) { $data['password'] = password::hash($data['password']); } static::save($file, $data); // return the created user project return new static($data['username']); } static protected function save($file, $data) { $yaml = '' . PHP_EOL . PHP_EOL; $yaml .= data::encode($data, 'yaml'); if(!f::write($file, $yaml)) { throw new Exception('The user account could not be saved'); } else { return true; } } static public function unauthorize() { s::remove('kirby_auth_secret'); s::remove('kirby_auth_username'); cookie::remove('kirby_auth'); } static public function current() { $cookey = cookie::get(s::$name . '_auth'); $username = s::get('kirby_auth_username'); if(empty($cookey)) { static::unauthorize(); return false; } if(s::get('kirby_auth_secret') !== sha1($username . $cookey)) { static::unauthorize(); return false; } // find the logged in user by token try { $user = new static($username); return $user; } catch(Exception $e) { static::unauthorize(); return false; } } public function __toString() { return (string)$this->username; } }