500 lines
18 KiB
PHP
500 lines
18 KiB
PHP
<?php
|
|
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
# ***** BEGIN LICENSE BLOCK *****
|
|
# This file is part of Plume Framework, a simple PHP Application Framework.
|
|
# Copyright (C) 2001-2007 Loic d'Anterroches and contributors.
|
|
#
|
|
# Plume Framework is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU Lesser General Public License as published by
|
|
# the Free Software Foundation; either version 2.1 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# Plume Framework is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU Lesser General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU Lesser General Public License
|
|
# along with this program; if not, write to the Free Software
|
|
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
#
|
|
# ***** END LICENSE BLOCK ***** */
|
|
|
|
require_once dirname(__FILE__).'/thirdparty/otp/otphp.php';
|
|
|
|
/**
|
|
* User Model.
|
|
*/
|
|
class Pluf_User extends Pluf_Model
|
|
{
|
|
public $_model = 'Pluf_User';
|
|
|
|
/**
|
|
* If the session contains this key with a numeric id in. The
|
|
* corresponding user is loaded.
|
|
*/
|
|
public $session_key = '_PX_Pluf_User_auth';
|
|
|
|
/**
|
|
* Cache of the permissions.
|
|
*/
|
|
public $_cache_perms = null;
|
|
|
|
function init()
|
|
{
|
|
$langs = Pluf::f('languages', array('en'));
|
|
$this->_a['verbose'] = __('user');
|
|
$this->_a['table'] = 'users';
|
|
$this->_a['model'] = 'Pluf_User';
|
|
$this->_a['cols'] = array(
|
|
// It is mandatory to have an "id" column.
|
|
'id' =>
|
|
array(
|
|
'type' => 'Pluf_DB_Field_Sequence',
|
|
//It is automatically added.
|
|
'blank' => true,
|
|
),
|
|
'login' =>
|
|
array(
|
|
'type' => 'Pluf_DB_Field_Varchar',
|
|
'blank' => false,
|
|
'unique' => true,
|
|
'size' => 50,
|
|
'verbose' => __('login'),
|
|
),
|
|
'first_name' =>
|
|
array(
|
|
'type' => 'Pluf_DB_Field_Varchar',
|
|
'blank' => true,
|
|
'size' => 100,
|
|
'verbose' => __('first name'),
|
|
),
|
|
'last_name' =>
|
|
array(
|
|
'type' => 'Pluf_DB_Field_Varchar',
|
|
'blank' => false,
|
|
'size' => 100,
|
|
'verbose' => __('last name'),
|
|
),
|
|
'email' =>
|
|
array(
|
|
'type' => 'Pluf_DB_Field_Email',
|
|
'blank' => false,
|
|
'verbose' => __('email'),
|
|
),
|
|
'password' =>
|
|
array(
|
|
'type' => 'Pluf_DB_Field_Password',
|
|
'blank' => false,
|
|
'verbose' => __('password'),
|
|
'size' => 150,
|
|
'help_text' => __('Format: [algo]:[salt]:[hash]')
|
|
),
|
|
'groups' =>
|
|
array(
|
|
'type' => 'Pluf_DB_Field_Manytomany',
|
|
'blank' => true,
|
|
'model' => Pluf::f('pluf_custom_group','Pluf_Group'),
|
|
'relate_name' => 'users',
|
|
),
|
|
'permissions' =>
|
|
array(
|
|
'type' => 'Pluf_DB_Field_Manytomany',
|
|
'blank' => true,
|
|
'model' => 'Pluf_Permission',
|
|
),
|
|
'administrator' =>
|
|
array(
|
|
'type' => 'Pluf_DB_Field_Boolean',
|
|
'default' => false,
|
|
'blank' => true,
|
|
'verbose' => __('administrator'),
|
|
),
|
|
'staff' =>
|
|
array(
|
|
'type' => 'Pluf_DB_Field_Boolean',
|
|
'default' => false,
|
|
'blank' => true,
|
|
'verbose' => __('staff'),
|
|
),
|
|
'active' =>
|
|
array(
|
|
'type' => 'Pluf_DB_Field_Boolean',
|
|
'default' => true,
|
|
'blank' => true,
|
|
'verbose' => __('active'),
|
|
),
|
|
'language' =>
|
|
array(
|
|
'type' => 'Pluf_DB_Field_Varchar',
|
|
'blank' => true,
|
|
'default' => $langs[0],
|
|
'size' => 5,
|
|
'verbose' => __('language'),
|
|
'help_text' => __('Prefered language of the user for the interface. Use the 2 or 5 letter code like "fr", "en", "fr_QC" or "en_US".')
|
|
),
|
|
'timezone' =>
|
|
array(
|
|
'type' => 'Pluf_DB_Field_Varchar',
|
|
'blank' => true,
|
|
'default' => 'America/Chicago',
|
|
'size' => 45,
|
|
'verbose' => __('time zone'),
|
|
'help_text' => __('Time zone of the user to display the time in local time.')
|
|
),
|
|
'date_joined' =>
|
|
array(
|
|
'type' => 'Pluf_DB_Field_Datetime',
|
|
'blank' => true,
|
|
'verbose' => __('date joined'),
|
|
'editable' => false,
|
|
),
|
|
'last_login' =>
|
|
array(
|
|
'type' => 'Pluf_DB_Field_Datetime',
|
|
'blank' => true,
|
|
'verbose' => __('last login'),
|
|
'editable' => false,
|
|
),
|
|
'otpkey' =>
|
|
array(
|
|
'type' => 'Pluf_DB_Field_Varchar',
|
|
'blank' => true,
|
|
'size' => 50,
|
|
'verbose' => __('OTP Key for user'),
|
|
'help_text' => __('OTP Key used for authentication against repos.')
|
|
),
|
|
);
|
|
$this->_a['idx'] = array(
|
|
'login_idx' =>
|
|
array(
|
|
'col' => 'login',
|
|
'type' => 'unique',
|
|
),
|
|
);
|
|
$this->_a['views'] = array();
|
|
if (Pluf::f('pluf_custom_user',false)) $this->extended_init();
|
|
}
|
|
|
|
/**
|
|
* Hook for extended class
|
|
*/
|
|
function extended_init()
|
|
{
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* String representation of the user.
|
|
*/
|
|
function __toString()
|
|
{
|
|
$repr = $this->last_name;
|
|
if (strlen($this->first_name) > 0) {
|
|
$repr = $this->first_name.' '.$repr;
|
|
}
|
|
return $repr;
|
|
}
|
|
|
|
/**
|
|
* Predelete to drop the row level permissions.
|
|
*/
|
|
function preDelete()
|
|
{
|
|
/**
|
|
* [signal]
|
|
*
|
|
* Pluf_User::preDelete
|
|
*
|
|
* [sender]
|
|
*
|
|
* Pluf_User
|
|
*
|
|
* [description]
|
|
*
|
|
* This signal allows an application to perform special
|
|
* operations at the deletion of a user.
|
|
*
|
|
* [parameters]
|
|
*
|
|
* array('user' => $user)
|
|
*
|
|
*/
|
|
$params = array('user' => $this);
|
|
Pluf_Signal::send('Pluf_User::preDelete',
|
|
'Pluf_User', $params);
|
|
|
|
if (Pluf::f('pluf_use_rowpermission', false)) {
|
|
$_rpt = Pluf::factory('Pluf_RowPermission')->getSqlTable();
|
|
$sql = new Pluf_SQL('owner_class=%s AND owner_id=%s',
|
|
array($this->_a['model'], $this->_data['id']));
|
|
$this->_con->execute('DELETE FROM '.$_rpt.' WHERE '.$sql->gen());
|
|
}
|
|
}
|
|
|
|
public function convertToUserTimezone($date) {
|
|
$currentDateTime = new \DateTime(date('Y-m-d H:i:s', strtotime($date.' GMT')));
|
|
$currentDateTime->setTimezone(new \DateTimeZone($this->timezone));
|
|
return $currentDateTime->format("M, j Y g:i:s A");
|
|
}
|
|
|
|
/**
|
|
* Set the password of a user.
|
|
*
|
|
* You need to manually save the user to store the password in the
|
|
* database. The supported algorithms are md5, crc32 and sha1,
|
|
* sha1 being the default.
|
|
*
|
|
* @param string New password
|
|
* @return bool Success
|
|
*/
|
|
function setPassword($password)
|
|
{
|
|
//$salt = Pluf_Utils::getRandomString(5);
|
|
//$this->password = 'sha1:'.$salt.':'.sha1($salt.$password);
|
|
$this->password = base64_encode(sha1($password, TRUE));
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Check if the password of a user matches with the one in the database.
|
|
*
|
|
* @param string Password
|
|
* @return bool True if success
|
|
*/
|
|
function checkPassword($password)
|
|
{
|
|
if ($this->password == '') {
|
|
return false;
|
|
}
|
|
if ($this->otpkey == "")
|
|
{
|
|
if ($this->password == base64_encode(sha1($password, TRUE)))
|
|
return true;
|
|
else
|
|
return false;
|
|
} else {
|
|
$otp = substr($password, 0, 6);
|
|
$pass = substr($password, 6);
|
|
$totp = new \OTPHP\TOTP(strtoupper(Pluf_Utils::convBase($this->otpkey, '0123456789abcdef', 'abcdefghijklmnopqrstuvwxyz234567')));
|
|
if ($totp->verify($otp) && $this->password == base64_encode(sha1($pass, TRUE)))
|
|
{
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
|
|
}
|
|
/*list($algo, $salt, $hash) = explode(':', $this->password);
|
|
if ($hash == $algo($salt.$password)) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}*/
|
|
}
|
|
|
|
/**
|
|
* Check if the login creditentials are valid.
|
|
*
|
|
* @param string Login
|
|
* @param string Password
|
|
* @return mixed False or matching user
|
|
*/
|
|
function checkCreditentials($login, $password)
|
|
{
|
|
$where = 'login = '.$this->_toDb($login, 'login');
|
|
$users = $this->getList(array('filter' => $where));
|
|
if ($users === false or count($users) !== 1) {
|
|
return false;
|
|
}
|
|
if ($users[0]->active and $users[0]->checkPassword($password)) {
|
|
return $users[0];
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Set the last_login and date_joined before creating.
|
|
*
|
|
*/
|
|
function preSave($create=false)
|
|
{
|
|
if (!($this->id > 0)) {
|
|
$this->last_login = gmdate('Y-m-d H:i:s');
|
|
$this->date_joined = gmdate('Y-m-d H:i:s');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if a user is anonymous.
|
|
*
|
|
* @return bool True if the user is anonymous.
|
|
*/
|
|
function isAnonymous()
|
|
{
|
|
return (0 === (int) $this->id);
|
|
}
|
|
|
|
/**
|
|
* Get all the permissions of a user.
|
|
*
|
|
* @param bool Force the reload of the list of permissions (false)
|
|
* @return array List of permissions
|
|
*/
|
|
function getAllPermissions($force=false)
|
|
{
|
|
if ($force==false and !is_null($this->_cache_perms)) {
|
|
return $this->_cache_perms;
|
|
}
|
|
$this->_cache_perms = array();
|
|
$perms = (array) $this->get_permissions_list();
|
|
$groups = $this->get_groups_list();
|
|
$ids = array();
|
|
foreach ($groups as $group) {
|
|
$ids[] = $group->id;
|
|
}
|
|
if (count($ids) > 0) {
|
|
$gperm = new Pluf_Permission();
|
|
$f_name = strtolower(Pluf::f('pluf_custom_group', 'Pluf_Group')).'_id';
|
|
$perms = array_merge($perms, (array) $gperm->getList(array('filter' => $f_name.' IN ('.join(', ', $ids).')',
|
|
'view' => 'join_group')));
|
|
}
|
|
foreach ($perms as $perm) {
|
|
if (!in_array($perm->application.'.'.$perm->code_name, $this->_cache_perms)) {
|
|
$this->_cache_perms[] = $perm->application.'.'.$perm->code_name;
|
|
}
|
|
}
|
|
if (Pluf::f('pluf_use_rowpermission', false) and $this->id) {
|
|
$growp = new Pluf_RowPermission();
|
|
$sql = new Pluf_SQL('owner_id=%s AND owner_class=%s',
|
|
array($this->id, 'Pluf_User'));
|
|
if (count($ids) > 0) {
|
|
$sql2 = new Pluf_SQL('owner_id IN ('.join(', ', $ids).') AND owner_class=%s',
|
|
array(Pluf::f('pluf_custom_group','Pluf_Group')));
|
|
$sql->SOr($sql2);
|
|
}
|
|
$perms = $growp->getList(array('filter' => $sql->gen(),
|
|
'view' => 'join_permission'));
|
|
foreach ($perms as $perm) {
|
|
$perm_string = $perm->application.'.'.$perm->code_name.'#'.$perm->model_class.'('.$perm->model_id.')';
|
|
if ($perm->negative) {
|
|
$perm_string = '!'.$perm_string;
|
|
}
|
|
if (!in_array($perm_string, $this->_cache_perms)) {
|
|
$this->_cache_perms[] = $perm_string;
|
|
}
|
|
}
|
|
}
|
|
return $this->_cache_perms;
|
|
}
|
|
|
|
/**
|
|
* Check if a user as a permission.
|
|
*
|
|
* @param string Permission
|
|
* @param Object Object for row level permission (null)
|
|
* @return bool True if the user has the permission.
|
|
*/
|
|
function hasPerm($perm, $obj=null)
|
|
{
|
|
if (!$this->active)
|
|
return false;
|
|
if ($this->administrator)
|
|
return true;
|
|
$perms = $this->getAllPermissions();
|
|
if (!is_null($obj)) {
|
|
$perm_row = $perm.'#'.$obj->_a['model'].'('.$obj->id.')';
|
|
if (in_array('!'.$perm_row, $perms))
|
|
return false;
|
|
if (in_array($perm_row, $perms))
|
|
return true;
|
|
}
|
|
if (in_array($perm, $perms))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Check if a user one or more application permission.
|
|
*
|
|
* @return bool True if the user has some.
|
|
*/
|
|
function hasAppPerms($app)
|
|
{
|
|
if ($this->administrator)
|
|
return true;
|
|
foreach ($this->getAllPermissions() as $perm) {
|
|
if (0 === strpos($perm, $app.'.')) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Set a message.
|
|
*
|
|
* The user must not be anonymous.
|
|
*
|
|
* @param string Message
|
|
* @return bool Success
|
|
*/
|
|
function setMessage($message)
|
|
{
|
|
if ($this->isAnonymous()) {
|
|
return false;
|
|
}
|
|
$m = new Pluf_Message();
|
|
$m->user = $this;
|
|
$m->message = $message;
|
|
return $m->create();
|
|
}
|
|
|
|
/**
|
|
* Get messages and delete them.
|
|
*
|
|
* The user must not be anonymous.
|
|
*
|
|
* @return mixed False if anonymous, else ArrayObject
|
|
*/
|
|
function getAndDeleteMessages()
|
|
{
|
|
if ($this->isAnonymous()) {
|
|
return false;
|
|
}
|
|
$messages = new ArrayObject();
|
|
$ms = $this->get_pluf_message_list();
|
|
foreach ($ms as $m) {
|
|
$messages[] = $m->message;
|
|
$m->delete();
|
|
}
|
|
return $messages;
|
|
}
|
|
|
|
/**
|
|
* Get profile.
|
|
*
|
|
* Retrieve the profile of the current user. If not profile in the
|
|
* database a Pluf_Exception_DoesNotExist exception is thrown,
|
|
* just catch it and create a profile.
|
|
*
|
|
* @return Pluf_Model User profile
|
|
*/
|
|
function getProfile()
|
|
{
|
|
$pclass = Pluf::f('user_profile_class', false);
|
|
if (false == $pclass) {
|
|
throw new Pluf_Exception_SettingError(__('"user_profile_class" setting not defined.'));
|
|
}
|
|
$db = $this->getDbConnection();
|
|
$sql = new Pluf_SQL(sprintf('%s=%%s', $db->qn('user')),
|
|
array($this->id));
|
|
$users = Pluf::factory($pclass)->getList(array('filter' => $sql->gen()));
|
|
if ($users->count() != 1) {
|
|
throw new Pluf_Exception_DoesNotExist(sprintf(__('No profiles available for user: %s'), (string) $this));
|
|
}
|
|
return $users[0];
|
|
}
|
|
}
|