Very basic user management
Added an user list page and a form to create new users. All users can create other users.
This commit is contained in:
parent
b6b5cd982e
commit
071577168a
7 changed files with 225 additions and 29 deletions
|
@ -6,6 +6,9 @@ BEGIN;
|
||||||
-- Tables from the main database structure
|
-- Tables from the main database structure
|
||||||
\i database/create-tables.sql
|
\i database/create-tables.sql
|
||||||
|
|
||||||
|
-- User functions
|
||||||
|
\i database/users-functions.sql
|
||||||
|
|
||||||
-- Items tree management and associated functions
|
-- Items tree management and associated functions
|
||||||
\i database/items-tree-triggers.sql
|
\i database/items-tree-triggers.sql
|
||||||
\i database/items-functions.sql
|
\i database/items-functions.sql
|
||||||
|
|
21
database/users-functions.sql
Normal file
21
database/users-functions.sql
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
--
|
||||||
|
-- Create a new user
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION users_add( _email TEXT , _salt TEXT , _iters INT , _hash TEXT )
|
||||||
|
RETURNS INT
|
||||||
|
LANGUAGE PLPGSQL
|
||||||
|
STRICT VOLATILE SECURITY INVOKER
|
||||||
|
AS $users_add$
|
||||||
|
BEGIN
|
||||||
|
INSERT INTO users ( user_email , user_salt , user_iterations , user_hash )
|
||||||
|
VALUES ( _email , _salt , _iters , _hash );
|
||||||
|
RETURN 0;
|
||||||
|
EXCEPTION
|
||||||
|
WHEN unique_violation THEN
|
||||||
|
RETURN 1;
|
||||||
|
END;
|
||||||
|
$users_add$;
|
||||||
|
|
||||||
|
REVOKE EXECUTE ON FUNCTION users_add( TEXT , TEXT , INT , TEXT ) FROM PUBLIC;
|
||||||
|
GRANT EXECUTE ON FUNCTION users_add( TEXT , TEXT , INT , TEXT) TO :webapp_user;
|
79
includes/t-basics/dao_users.inc.php
Normal file
79
includes/t-basics/dao_users.inc.php
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
class Dao_Users
|
||||||
|
extends DAO
|
||||||
|
{
|
||||||
|
private function hashPassword( $password , $salt , $iterations )
|
||||||
|
{
|
||||||
|
$hash = $password;
|
||||||
|
$salt = trim( $salt );
|
||||||
|
do {
|
||||||
|
$hash = sha1( "$salt$hash$salt" );
|
||||||
|
$iterations --;
|
||||||
|
} while ( $iterations > 0 );
|
||||||
|
return $hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function getUsers( )
|
||||||
|
{
|
||||||
|
return $this->query( 'SELECT user_id , user_email FROM users ORDER BY LOWER( user_email )' )->execute( );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function getUser( $email )
|
||||||
|
{
|
||||||
|
$query = $this->query( 'SELECT * FROM users WHERE user_email = LOWER( $1 )' );
|
||||||
|
$results = $query->execute( $email );
|
||||||
|
if ( empty( $results ) ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return array_shift( $results );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function checkLogin( $email , $password )
|
||||||
|
{
|
||||||
|
$userData = $this->getUser( $email );
|
||||||
|
if ( $userData != null ) {
|
||||||
|
$hashed = $this->hashPassword( $password ,
|
||||||
|
$userData->user_salt ,
|
||||||
|
$userData->user_iterations );
|
||||||
|
if ( $hashed === $userData->user_hash ) {
|
||||||
|
return $userData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function addUser( $email , $password )
|
||||||
|
{
|
||||||
|
$iterations = rand( 130 , 160 );
|
||||||
|
|
||||||
|
$randSource = array( );
|
||||||
|
for ( $i = 0 ; $i < 26 ; $i ++ ) {
|
||||||
|
array_push( $randSource , chr( $i + ord( 'a' ) ) );
|
||||||
|
array_push( $randSource , chr( $i + ord( 'A' ) ) );
|
||||||
|
if ( $i < 10 ) {
|
||||||
|
array_push( $randSource , chr( $i + 48 ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
shuffle( $randSource );
|
||||||
|
$salt = join( '' , array_splice( $randSource , 0 , 4 ) );
|
||||||
|
|
||||||
|
$hash = $this->hashPassword( $password , $salt , $iterations );
|
||||||
|
|
||||||
|
$result = $this->query( 'SELECT users_add( $1 , $2 , $3 , $4 ) AS error' )
|
||||||
|
->execute( $email , $salt , $iterations , $hash );
|
||||||
|
return $result[ 0 ]->error;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function hasUsers( )
|
||||||
|
{
|
||||||
|
$result = $this->query( 'SELECT COUNT(*) AS n_users FROM users' )->execute( );
|
||||||
|
return $result[0]->n_users;
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,8 +2,8 @@
|
||||||
|
|
||||||
$package[ 'requires' ][] = 'form';
|
$package[ 'requires' ][] = 'form';
|
||||||
$package[ 'requires' ][] = 'hub-page';
|
$package[ 'requires' ][] = 'hub-page';
|
||||||
$package[ 'requires' ][] = 't-users';
|
|
||||||
|
|
||||||
|
$package[ 'files' ][] = 'dao_users';
|
||||||
$package[ 'files' ][] = 'controllers';
|
$package[ 'files' ][] = 'controllers';
|
||||||
$package[ 'files' ][] = 'pages';
|
$package[ 'files' ][] = 'pages';
|
||||||
|
|
||||||
|
@ -20,3 +20,4 @@ $package[ 'pages' ][] = 'tasks_home';
|
||||||
$package[ 'pages' ][] = 'tasks_login';
|
$package[ 'pages' ][] = 'tasks_login';
|
||||||
$package[ 'pages' ][] = 'tasks_logout';
|
$package[ 'pages' ][] = 'tasks_logout';
|
||||||
|
|
||||||
|
$package[ 'daos' ][] = 'users';
|
||||||
|
|
|
@ -14,6 +14,7 @@ abstract class AuthenticatedPage
|
||||||
return array(
|
return array(
|
||||||
'items' => 'Items' ,
|
'items' => 'Items' ,
|
||||||
'tasks' => 'Tasks' ,
|
'tasks' => 'Tasks' ,
|
||||||
|
'users' => 'Users' ,
|
||||||
'logout' => 'Log out'
|
'logout' => 'Log out'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,13 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
$package[ 'requires' ][] = 'core';
|
$package[ 'requires' ][] = 't-basics';
|
||||||
|
|
||||||
$package[ 'files' ][] = 'users';
|
$package[ 'files' ][] = 'users';
|
||||||
$package[ 'daos' ][] = 'users';
|
|
||||||
|
$package[ 'pages' ][] = 'tasks_users';
|
||||||
|
|
||||||
|
$package[ 'ctrls' ][] = 'users_list';
|
||||||
|
$package[ 'ctrls' ][] = 'users_add';
|
||||||
|
$package[ 'ctrls' ][] = 'users_add_form';
|
||||||
|
|
||||||
|
$package[ 'views' ][] = 'users_list';
|
||||||
|
|
|
@ -1,43 +1,126 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
|
||||||
class Dao_Users
|
class Page_TasksUsers
|
||||||
extends DAO
|
extends AuthenticatedPage
|
||||||
{
|
{
|
||||||
private function hashPassword( $password , $salt , $iterations )
|
public function __construct( )
|
||||||
{
|
{
|
||||||
$hash = $password;
|
parent::__construct( array(
|
||||||
$salt = trim( $salt );
|
'' => 'users_list' ,
|
||||||
do {
|
'add' => 'users_add_form' ,
|
||||||
$hash = sha1( "$salt$hash$salt" );
|
) );
|
||||||
$iterations --;
|
$this->setTitle( 'Users' );
|
||||||
} while ( $iterations > 0 );
|
}
|
||||||
return $hash;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function getUser( $email )
|
class Ctrl_UsersList
|
||||||
|
extends Controller
|
||||||
{
|
{
|
||||||
$query = $this->query( 'SELECT * FROM users WHERE user_email = LOWER( $1 )' );
|
|
||||||
$results = $query->execute( $email );
|
public function handle( Page $page )
|
||||||
if ( empty( $results ) ) {
|
{
|
||||||
|
$dao = Loader::DAO( 'users' );
|
||||||
|
return Loader::View( 'box' , null , Loader::View( 'users_list' , $dao->getUsers( ) ) )
|
||||||
|
->setClass( 'list' )
|
||||||
|
->addButton( BoxButton::create( 'Add user' , 'users/add' )
|
||||||
|
->setClass( 'list-add' ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class Ctrl_UsersAddForm
|
||||||
|
extends Controller
|
||||||
|
{
|
||||||
|
public function handle( Page $page )
|
||||||
|
{
|
||||||
|
return Loader::Create( 'Form' , 'Create user' , 'user-add' )
|
||||||
|
->addField( Loader::Create( 'Field' , 'email' , 'text' )
|
||||||
|
->setDescription( 'E-mail address:' )
|
||||||
|
->setValidator( Loader::Create( 'Validator_Email' , 'Invalid address.' ) ) )
|
||||||
|
->addField( Loader::Create( 'Field' , 'pass' , 'password' )
|
||||||
|
->setDescription( 'Password:' )
|
||||||
|
->setValidator( Loader::Create( 'Validator_StringLength' , 'This password' , 8 ) ) )
|
||||||
|
->addField( Loader::Create( 'Field' , 'pass2' , 'password' )
|
||||||
|
->setDescription( 'Confirm password:' ) )
|
||||||
|
->setURL( 'users' )
|
||||||
|
->addController( Loader::Ctrl( 'users_add' ) )
|
||||||
|
->controller( );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class Ctrl_UsersAdd
|
||||||
|
extends Controller
|
||||||
|
implements FormAware
|
||||||
|
{
|
||||||
|
private $form;
|
||||||
|
|
||||||
|
public function setForm( Form $form )
|
||||||
|
{
|
||||||
|
$this->form = $form;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function handle( Page $page )
|
||||||
|
{
|
||||||
|
$p1 = $this->form->field( 'pass' );
|
||||||
|
$p2 = $this->form->field( 'pass2' );
|
||||||
|
if ( $p1->value( ) != $p2->value( ) ) {
|
||||||
|
$p1->putError( 'Passwords did not match.' );
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return array_shift( $results );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
$email = $this->form->field( 'email' );
|
||||||
|
$error = Loader::DAO( 'users' )->addUser( $email->value( ) ,
|
||||||
|
$p1->value( ) );
|
||||||
|
|
||||||
public function checkLogin( $email , $password )
|
switch ( $error ) {
|
||||||
{
|
|
||||||
$userData = $this->getUser( $email );
|
case 0:
|
||||||
if ( $userData != null ) {
|
return true;
|
||||||
$hashed = $this->hashPassword( $password ,
|
|
||||||
$userData->user_salt ,
|
case 1:
|
||||||
$userData->user_iterations );
|
$email->putError( 'This e-mail address is already in use.' );
|
||||||
if ( $hashed === $userData->user_hash ) {
|
break;
|
||||||
return $userData;
|
|
||||||
}
|
default:
|
||||||
|
$email->putError( 'Some unknown error has occurred (' . $error . ')' );
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class View_UsersList
|
||||||
|
extends BaseURLAwareView
|
||||||
|
{
|
||||||
|
private $users;
|
||||||
|
|
||||||
|
public function __construct( $users )
|
||||||
|
{
|
||||||
|
$this->users = $users;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render( )
|
||||||
|
{
|
||||||
|
$table = HTML::make( 'table' )
|
||||||
|
->appendElement( HTML::make( 'tr' )
|
||||||
|
->setAttribute( 'class' , 'header' )
|
||||||
|
->appendElement( HTML::make( 'th' )
|
||||||
|
->appendText( 'E-mail address' ) ) );
|
||||||
|
|
||||||
|
foreach ( $this->users as $user ) {
|
||||||
|
$table->appendElement( HTML::make( 'tr' )
|
||||||
|
->appendElement( HTML::make( 'td' )
|
||||||
|
->appendText( $user->user_email ) ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
return $table;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue