Ask the current password before changing a user password

This commit is contained in:
Frédéric Guillot
2014-03-25 21:25:54 -04:00
parent 0d55f5aa35
commit 34711f5846
10 changed files with 231 additions and 36 deletions

View File

@@ -156,9 +156,6 @@ textarea {
font-size: 99%; font-size: 99%;
} }
select {
}
::-webkit-input-placeholder { ::-webkit-input-placeholder {
color: #bbb; color: #bbb;
padding-top: 2px; padding-top: 2px;
@@ -183,6 +180,12 @@ textarea.form-error {
border: 2px solid #b94a48; border: 2px solid #b94a48;
} }
.form-required {
color: red;
padding-left: 5px;
font-weight: bold;
}
.form-errors { .form-errors {
color: #b94a48; color: #b94a48;
list-style-type: none; list-style-type: none;

View File

@@ -4,9 +4,19 @@ namespace Controller;
require_once __DIR__.'/base.php'; require_once __DIR__.'/base.php';
/**
* User controller
*
* @package controller
* @author Frederic Guillot
*/
class User extends Base class User extends Base
{ {
// Display access forbidden page /**
* Display access forbidden page
*
* @access public
*/
public function forbidden() public function forbidden()
{ {
$this->response->html($this->template->layout('user_forbidden', array( $this->response->html($this->template->layout('user_forbidden', array(
@@ -15,14 +25,22 @@ class User extends Base
))); )));
} }
// Logout and destroy session /**
* Logout and destroy session
*
* @access public
*/
public function logout() public function logout()
{ {
$this->session->close(); $this->session->close();
$this->response->redirect('?controller=user&action=login'); $this->response->redirect('?controller=user&action=login');
} }
// Display the form login /**
* Display the form login
*
* @access public
*/
public function login() public function login()
{ {
if (isset($_SESSION['user'])) $this->response->redirect('?controller=app'); if (isset($_SESSION['user'])) $this->response->redirect('?controller=app');
@@ -35,7 +53,11 @@ class User extends Base
))); )));
} }
// Check credentials /**
* Check credentials
*
* @access public
*/
public function check() public function check()
{ {
$values = $this->request->getValues(); $values = $this->request->getValues();
@@ -51,7 +73,11 @@ class User extends Base
))); )));
} }
// List all users /**
* List all users
*
* @access public
*/
public function index() public function index()
{ {
$users = $this->user->getAll(); $users = $this->user->getAll();
@@ -67,7 +93,11 @@ class User extends Base
))); )));
} }
// Display a form to create a new user /**
* Display a form to create a new user
*
* @access public
*/
public function create() public function create()
{ {
$this->response->html($this->template->layout('user_new', array( $this->response->html($this->template->layout('user_new', array(
@@ -79,7 +109,11 @@ class User extends Base
))); )));
} }
// Validate and save a new user /**
* Validate and save a new user
*
* @access public
*/
public function save() public function save()
{ {
$values = $this->request->getValues(); $values = $this->request->getValues();
@@ -105,14 +139,18 @@ class User extends Base
))); )));
} }
// Display a form to edit a user /**
* Display a form to edit a user
*
* @access public
*/
public function edit() public function edit()
{ {
$user = $this->user->getById($this->request->getIntegerParam('user_id')); $user = $this->user->getById($this->request->getIntegerParam('user_id'));
if (! $user) $this->notfound(); if (! $user) $this->notfound();
if (! $_SESSION['user']['is_admin'] && $_SESSION['user']['id'] != $user['id']) { if ($this->acl->isRegularUser() && $this->acl->getUserId() != $user['id']) {
$this->forbidden(); $this->forbidden();
} }
@@ -127,17 +165,21 @@ class User extends Base
))); )));
} }
// Validate and update a user /**
* Validate and update a user
*
* @access public
*/
public function update() public function update()
{ {
$values = $this->request->getValues(); $values = $this->request->getValues();
if ($_SESSION['user']['is_admin'] == 1) { if ($this->acl->isAdminUser()) {
$values += array('is_admin' => 0); $values += array('is_admin' => 0);
} }
else { else {
if ($_SESSION['user']['id'] != $values['id']) { if ($this->acl->getUserId() != $values['id']) {
$this->forbidden(); $this->forbidden();
} }
@@ -168,7 +210,11 @@ class User extends Base
))); )));
} }
// Confirmation dialog before to remove a user /**
* Confirmation dialog before to remove a user
*
* @access public
*/
public function confirm() public function confirm()
{ {
$user = $this->user->getById($this->request->getIntegerParam('user_id')); $user = $this->user->getById($this->request->getIntegerParam('user_id'));
@@ -182,7 +228,11 @@ class User extends Base
))); )));
} }
// Remove a user /**
* Remove a user
*
* @access public
*/
public function remove() public function remove()
{ {
$user_id = $this->request->getIntegerParam('user_id'); $user_id = $this->request->getIntegerParam('user_id');

View File

@@ -18,6 +18,11 @@ function is_admin()
return $_SESSION['user']['is_admin'] == 1; return $_SESSION['user']['is_admin'] == 1;
} }
function get_username()
{
return $_SESSION['user']['username'];
}
function markdown($text) function markdown($text)
{ {
require_once __DIR__.'/../vendor/Michelf/MarkdownExtra.inc.php'; require_once __DIR__.'/../vendor/Michelf/MarkdownExtra.inc.php';
@@ -206,6 +211,7 @@ function form_textarea($name, $values = array(), array $errors = array(), array
$html .= implode(' ', $attributes).'>'; $html .= implode(' ', $attributes).'>';
$html .= isset($values->$name) ? escape($values->$name) : isset($values[$name]) ? $values[$name] : ''; $html .= isset($values->$name) ? escape($values->$name) : isset($values[$name]) ? $values[$name] : '';
$html .= '</textarea>'; $html .= '</textarea>';
if (in_array('required', $attributes)) $html .= '<span class="form-required">*</span>';
$html .= error_list($errors, $name); $html .= error_list($errors, $name);
return $html; return $html;
@@ -217,6 +223,7 @@ function form_input($type, $name, $values = array(), array $errors = array(), ar
$html = '<input type="'.$type.'" name="'.$name.'" id="form-'.$name.'" '.form_value($values, $name).' class="'.$class.'" '; $html = '<input type="'.$type.'" name="'.$name.'" id="form-'.$name.'" '.form_value($values, $name).' class="'.$class.'" ';
$html .= implode(' ', $attributes).'/>'; $html .= implode(' ', $attributes).'/>';
if (in_array('required', $attributes)) $html .= '<span class="form-required">*</span>';
$html .= error_list($errors, $name); $html .= error_list($errors, $name);
return $html; return $html;

View File

@@ -264,4 +264,7 @@ return array(
'Do you really want to remove this comment?' => 'Voulez-vous vraiment supprimer ce commentaire ?', 'Do you really want to remove this comment?' => 'Voulez-vous vraiment supprimer ce commentaire ?',
'Only administrators or the creator of the comment can access to this page.' => 'Uniquement les administrateurs ou le créateur du commentaire peuvent accéder à cette page.', 'Only administrators or the creator of the comment can access to this page.' => 'Uniquement les administrateurs ou le créateur du commentaire peuvent accéder à cette page.',
'Details' => 'Détails', 'Details' => 'Détails',
'Current password for the user "%s"' => 'Mot de passe actuel pour l\'utilisateur « %s »',
'The current password is required' => 'Le mot de passe actuel est obligatoire',
'Wrong password' => 'Mauvais mot de passe',
); );

View File

@@ -267,4 +267,7 @@ return array(
// 'Do you really want to remove this comment?' => '', // 'Do you really want to remove this comment?' => '',
// 'Only administrators or the creator of the comment can access to this page.' => '', // 'Only administrators or the creator of the comment can access to this page.' => '',
// 'Details' => '', // 'Details' => '',
// 'Current password for the user "%s"' => '',
// 'The current password is required' => '',
// 'Wrong password' => '',
); );

View File

@@ -7,20 +7,51 @@ require_once __DIR__.'/base.php';
use \SimpleValidator\Validator; use \SimpleValidator\Validator;
use \SimpleValidator\Validators; use \SimpleValidator\Validators;
/**
* User model
*
* @package model
* @author Frederic Guillot
*/
class User extends Base class User extends Base
{ {
/**
* SQL table name
*
* @var string
*/
const TABLE = 'users'; const TABLE = 'users';
/**
* Get a specific user by id
*
* @access public
* @param integer $user_id User id
* @return array
*/
public function getById($user_id) public function getById($user_id)
{ {
return $this->db->table(self::TABLE)->eq('id', $user_id)->findOne(); return $this->db->table(self::TABLE)->eq('id', $user_id)->findOne();
} }
/**
* Get a specific user by the username
*
* @access public
* @param string $username Username
* @return array
*/
public function getByUsername($username) public function getByUsername($username)
{ {
return $this->db->table(self::TABLE)->eq('username', $username)->findOne(); return $this->db->table(self::TABLE)->eq('username', $username)->findOne();
} }
/**
* Get all users
*
* @access public
* @return array
*/
public function getAll() public function getAll()
{ {
return $this->db return $this->db
@@ -30,11 +61,24 @@ class User extends Base
->findAll(); ->findAll();
} }
/**
* List all users (key-value pairs with id/username)
*
* @access public
* @return array
*/
public function getList() public function getList()
{ {
return $this->db->table(self::TABLE)->asc('username')->listing('id', 'username'); return $this->db->table(self::TABLE)->asc('username')->listing('id', 'username');
} }
/**
* Add a new user in the database
*
* @access public
* @param array $values Form values
* @return boolean
*/
public function create(array $values) public function create(array $values)
{ {
if (isset($values['confirmation'])) unset($values['confirmation']); if (isset($values['confirmation'])) unset($values['confirmation']);
@@ -43,6 +87,13 @@ class User extends Base
return $this->db->table(self::TABLE)->save($values); return $this->db->table(self::TABLE)->save($values);
} }
/**
* Modify a new user
*
* @access public
* @param array $values Form values
* @return array
*/
public function update(array $values) public function update(array $values)
{ {
if (! empty($values['password'])) { if (! empty($values['password'])) {
@@ -52,17 +103,25 @@ class User extends Base
unset($values['password']); unset($values['password']);
} }
unset($values['confirmation']); if (isset($values['confirmation'])) unset($values['confirmation']);
if (isset($values['current_password'])) unset($values['current_password']);
$this->db->table(self::TABLE)->eq('id', $values['id'])->save($values); $result = $this->db->table(self::TABLE)->eq('id', $values['id'])->update($values);
if ($_SESSION['user']['id'] == $values['id']) { if ($_SESSION['user']['id'] == $values['id']) {
$this->updateSession(); $this->updateSession();
} }
return true; return $result;
} }
/**
* Remove a specific user
*
* @access public
* @param integer $user_id User id
* @return boolean
*/
public function remove($user_id) public function remove($user_id)
{ {
$this->db->startTransaction(); $this->db->startTransaction();
@@ -76,17 +135,32 @@ class User extends Base
return true; return true;
} }
/**
* Update user session information
*
* @access public
* @param array $user User data
*/
public function updateSession(array $user = array()) public function updateSession(array $user = array())
{ {
if (empty($user)) { if (empty($user)) {
$user = $this->getById($_SESSION['user']['id']); $user = $this->getById($_SESSION['user']['id']);
} }
if (isset($user['password'])) unset($user['password']); if (isset($user['password'])) {
unset($user['password']);
}
$_SESSION['user'] = $user; $_SESSION['user'] = $user;
} }
/**
* Validate user creation
*
* @access public
* @param array $values Form values
* @return array $valid, $errors [0] = Success or not, [1] = List of errors
*/
public function validateCreation(array $values) public function validateCreation(array $values)
{ {
$v = new Validator($values, array( $v = new Validator($values, array(
@@ -108,23 +182,28 @@ class User extends Base
); );
} }
/**
* Validate user modification
*
* @access public
* @param array $values Form values
* @return array $valid, $errors [0] = Success or not, [1] = List of errors
*/
public function validateModification(array $values) public function validateModification(array $values)
{ {
if (! empty($values['password'])) { if (! empty($values['password'])) {
return $this->validateCreation($values); return $this->validatePasswordModification($values);
} }
else {
$v = new Validator($values, array( $v = new Validator($values, array(
new Validators\Required('id', t('The user id is required')), new Validators\Required('id', t('The user id is required')),
new Validators\Required('username', t('The username is required')), new Validators\Required('username', t('The username is required')),
new Validators\MaxLength('username', t('The maximum length is %d characters', 50), 50), new Validators\MaxLength('username', t('The maximum length is %d characters', 50), 50),
new Validators\AlphaNumeric('username', t('The username must be alphanumeric')), new Validators\AlphaNumeric('username', t('The username must be alphanumeric')),
new Validators\Unique('username', t('The username must be unique'), $this->db->getConnection(), self::TABLE, 'id'), new Validators\Unique('username', t('The username must be unique'), $this->db->getConnection(), self::TABLE, 'id'),
new Validators\Integer('default_project_id', t('This value must be an integer')), new Validators\Integer('default_project_id', t('This value must be an integer')),
new Validators\Integer('is_admin', t('This value must be an integer')), new Validators\Integer('is_admin', t('This value must be an integer')),
)); ));
}
return array( return array(
$v->execute(), $v->execute(),
@@ -132,6 +211,53 @@ class User extends Base
); );
} }
/**
* Validate password modification
*
* @access public
* @param array $values Form values
* @return array $valid, $errors [0] = Success or not, [1] = List of errors
*/
public function validatePasswordModification(array $values)
{
$v = new Validator($values, array(
new Validators\Required('id', t('The user id is required')),
new Validators\Required('username', t('The username is required')),
new Validators\MaxLength('username', t('The maximum length is %d characters', 50), 50),
new Validators\AlphaNumeric('username', t('The username must be alphanumeric')),
new Validators\Unique('username', t('The username must be unique'), $this->db->getConnection(), self::TABLE, 'id'),
new Validators\Required('current_password', t('The current password is required')),
new Validators\Required('password', t('The password is required')),
new Validators\MinLength('password', t('The minimum length is %d characters', 6), 6),
new Validators\Required('confirmation', t('The confirmation is required')),
new Validators\Equals('password', 'confirmation', t('Passwords doesn\'t matches')),
new Validators\Integer('default_project_id', t('This value must be an integer')),
new Validators\Integer('is_admin', t('This value must be an integer')),
));
if ($v->execute()) {
// Check password
$user = $this->getById($_SESSION['user']['id']);
if ($user !== false && \password_verify($values['current_password'], $user['password'])) {
return array(true, array());
}
else {
return array(false, array('current_password' => array(t('Wrong password'))));
}
}
return array(false, $v->getErrors());
}
/**
* Validate user login
*
* @access public
* @param array $values Form values
* @return array $valid, $errors [0] = Success or not, [1] = List of errors
*/
public function validateLogin(array $values) public function validateLogin(array $values)
{ {
$v = new Validator($values, array( $v = new Validator($values, array(

View File

@@ -9,7 +9,7 @@
<form method="post" action="?controller=project&amp;action=save" autocomplete="off"> <form method="post" action="?controller=project&amp;action=save" autocomplete="off">
<?= Helper\form_label(t('Name'), 'name') ?> <?= Helper\form_label(t('Name'), 'name') ?>
<?= Helper\form_text('name', $values, $errors, array('autofocus required')) ?> <?= Helper\form_text('name', $values, $errors, array('autofocus', 'required')) ?>
<div class="form-actions"> <div class="form-actions">
<input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/> <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/>

View File

@@ -6,7 +6,7 @@
<form method="post" action="?controller=task&amp;action=save" autocomplete="off"> <form method="post" action="?controller=task&amp;action=save" autocomplete="off">
<?= Helper\form_label(t('Title'), 'title') ?> <?= Helper\form_label(t('Title'), 'title') ?>
<?= Helper\form_text('title', $values, $errors, array('autofocus required')) ?><br/> <?= Helper\form_text('title', $values, $errors, array('autofocus', 'required')) ?><br/>
<?= Helper\form_hidden('project_id', $values) ?> <?= Helper\form_hidden('project_id', $values) ?>

View File

@@ -13,6 +13,9 @@
<?= Helper\form_label(t('Username'), 'username') ?> <?= Helper\form_label(t('Username'), 'username') ?>
<?= Helper\form_text('username', $values, $errors, array('required')) ?><br/> <?= Helper\form_text('username', $values, $errors, array('required')) ?><br/>
<?= Helper\form_label(t('Current password for the user "%s"', Helper\get_username()), 'current_password') ?>
<?= Helper\form_password('current_password', $values, $errors) ?><br/>
<?= Helper\form_label(t('Password'), 'password') ?> <?= Helper\form_label(t('Password'), 'password') ?>
<?= Helper\form_password('password', $values, $errors) ?><br/> <?= Helper\form_password('password', $values, $errors) ?><br/>

View File

@@ -9,7 +9,7 @@
<form method="post" action="?controller=user&amp;action=save" autocomplete="off"> <form method="post" action="?controller=user&amp;action=save" autocomplete="off">
<?= Helper\form_label(t('Username'), 'username') ?> <?= Helper\form_label(t('Username'), 'username') ?>
<?= Helper\form_text('username', $values, $errors, array('autofocus required')) ?><br/> <?= Helper\form_text('username', $values, $errors, array('autofocus', 'required')) ?><br/>
<?= Helper\form_label(t('Password'), 'password') ?> <?= Helper\form_label(t('Password'), 'password') ?>
<?= Helper\form_password('password', $values, $errors, array('required')) ?><br/> <?= Helper\form_password('password', $values, $errors, array('required')) ?><br/>