From 188162f0a2782f9f5230f35bdbc6ccdbc8f9a02c Mon Sep 17 00:00:00 2001 From: Dan Cryer Date: Fri, 10 May 2013 16:25:51 +0100 Subject: [PATCH] Adding user accounts. --- PHPCI/Application.php | 26 +++ PHPCI/Controller/BuildController.php | 5 + PHPCI/Controller/ProjectController.php | 15 ++ PHPCI/Controller/SessionController.php | 59 +++++++ PHPCI/Controller/UserController.php | 167 ++++++++++++++++++++ PHPCI/Helper/User.php | 12 ++ PHPCI/Model/Base/UserBase.php | 210 +++++++++++++++++++++++++ PHPCI/Model/User.php | 20 +++ PHPCI/Store/Base/UserStoreBase.php | 68 ++++++++ PHPCI/Store/UserStore.php | 20 +++ PHPCI/View/Build.phtml | 4 +- PHPCI/View/BuildsTable.phtml | 2 + PHPCI/View/Index.phtml | 1 + PHPCI/View/Layout.phtml | 3 +- PHPCI/View/Login.phtml | 53 +++++++ PHPCI/View/Project.phtml | 7 +- PHPCI/View/User.phtml | 65 ++++++++ PHPCI/View/UserForm.phtml | 18 +++ README.md | 2 + composer.json | 1 + index.php | 2 + install.php | 62 ++++++-- 22 files changed, 803 insertions(+), 19 deletions(-) create mode 100644 PHPCI/Controller/SessionController.php create mode 100644 PHPCI/Controller/UserController.php create mode 100644 PHPCI/Helper/User.php create mode 100644 PHPCI/Model/Base/UserBase.php create mode 100644 PHPCI/Model/User.php create mode 100644 PHPCI/Store/Base/UserStoreBase.php create mode 100644 PHPCI/Store/UserStore.php create mode 100644 PHPCI/View/Login.phtml create mode 100644 PHPCI/View/User.phtml create mode 100644 PHPCI/View/UserForm.phtml diff --git a/PHPCI/Application.php b/PHPCI/Application.php index 01c8e85d..dc9dda12 100644 --- a/PHPCI/Application.php +++ b/PHPCI/Application.php @@ -8,9 +8,35 @@ class Application extends b8\Application { public function handleRequest() { + $controllerName = \b8\Registry::getInstance()->get('ControllerName'); + + if(!in_array($controllerName, array('Bitbucket', 'Github')) && !($controllerName == 'Session' && in_array($this->action, array('login', 'logout')))) + { + $this->validateSession(); + } + $view = new b8\View('Layout'); $view->content = parent::handleRequest(); return $view->render(); } + + protected function validateSession() + { + if(!empty($_SESSION['user_id'])) + { + $user = b8\Store\Factory::getStore('User')->getByPrimaryKey($_SESSION['user_id']); + + if($user) + { + Registry::getInstance()->set('user', $user); + return; + } + + unset($_SESSION['user_id']); + } + + header('Location: /session/login'); + die; + } } \ No newline at end of file diff --git a/PHPCI/Controller/BuildController.php b/PHPCI/Controller/BuildController.php index c80d584c..49802f38 100644 --- a/PHPCI/Controller/BuildController.php +++ b/PHPCI/Controller/BuildController.php @@ -59,6 +59,11 @@ class BuildController extends b8\Controller public function delete($buildId) { + if(!Registry::getInstance()->get('user')->getIsAdmin()) + { + throw new \Exception('You do not have permission to do that.'); + } + $build = $this->_buildStore->getById($buildId); $this->_buildStore->delete($build); diff --git a/PHPCI/Controller/ProjectController.php b/PHPCI/Controller/ProjectController.php index 197b33ba..d0834df8 100644 --- a/PHPCI/Controller/ProjectController.php +++ b/PHPCI/Controller/ProjectController.php @@ -46,6 +46,11 @@ class ProjectController extends b8\Controller public function delete($id) { + if(!Registry::getInstance()->get('user')->getIsAdmin()) + { + throw new \Exception('You do not have permission to do that.'); + } + $project = $this->_projectStore->getById($id); $this->_projectStore->delete($project); @@ -69,6 +74,11 @@ class ProjectController extends b8\Controller public function add() { + if(!Registry::getInstance()->get('user')->getIsAdmin()) + { + throw new \Exception('You do not have permission to do that.'); + } + $method = Registry::getInstance()->get('requestMethod'); if($method == 'POST') @@ -114,6 +124,11 @@ class ProjectController extends b8\Controller public function edit($id) { + if(!Registry::getInstance()->get('user')->getIsAdmin()) + { + throw new \Exception('You do not have permission to do that.'); + } + $method = Registry::getInstance()->get('requestMethod'); $project = $this->_projectStore->getById($id); diff --git a/PHPCI/Controller/SessionController.php b/PHPCI/Controller/SessionController.php new file mode 100644 index 00000000..0551dd4e --- /dev/null +++ b/PHPCI/Controller/SessionController.php @@ -0,0 +1,59 @@ +_userStore = b8\Store\Factory::getStore('User'); + } + + public function login() + { + if(b8\Registry::getInstance()->get('requestMethod') == 'POST') + { + $user = $this->_userStore->getByEmail($this->getParam('email')); + + if($user && password_verify($this->getParam('password', ''), $user->getHash())) + { + $_SESSION['user_id'] = $user->getId(); + header('Location: /'); + die; + } + } + + $form = new b8\Form(); + $form->setMethod('POST'); + $form->setAction('/session/login'); + + $email = new b8\Form\Element\Email('email'); + $email->setLabel('Email Address'); + $email->setRequired(true); + $email->setClass('span3'); + $form->addField($email); + + $pwd = new b8\Form\Element\Password('password'); + $pwd->setLabel('Password'); + $pwd->setRequired(true); + $pwd->setClass('span3'); + $form->addField($pwd); + + $pwd = new b8\Form\Element\Submit(); + $pwd->setValue('Login »'); + $pwd->setClass('btn-success'); + $form->addField($pwd); + + $view = new b8\View('Login'); + $view->form = $form->render(); + die($view->render()); + } + + public function logout() + { + unset($_SESSION['user_id']); + header('Location: /'); + die; + } +} \ No newline at end of file diff --git a/PHPCI/Controller/UserController.php b/PHPCI/Controller/UserController.php new file mode 100644 index 00000000..fd6901f6 --- /dev/null +++ b/PHPCI/Controller/UserController.php @@ -0,0 +1,167 @@ +_userStore = b8\Store\Factory::getStore('User'); + } + + public function index() + { + $users = $this->_userStore->getWhere(array(), 1000, 0, array(), array('email' => 'ASC')); + $view = new b8\View('User'); + $view->users = $users; + + return $view->render(); + } + + public function add() + { + if(!Registry::getInstance()->get('user')->getIsAdmin()) + { + throw new \Exception('You do not have permission to do that.'); + } + + $method = Registry::getInstance()->get('requestMethod'); + + if($method == 'POST') + { + $values = $this->getParams(); + } + else + { + $values = array(); + } + + $form = $this->userForm($values); + + if($method != 'POST' || ($method == 'POST' && !$form->validate())) + { + $view = new b8\View('UserForm'); + $view->type = 'add'; + $view->user = null; + $view->form = $form; + + return $view->render(); + } + + $values = $form->getValues(); + $values['is_admin'] = $values['admin'] ? 1 : 0; + $values['hash'] = password_hash($values['password'], PASSWORD_DEFAULT); + + $user = new User(); + $user->setValues($values); + + $user = $this->_userStore->save($user); + + header('Location: /user'); + die; + } + + public function edit($id) + { + if(!Registry::getInstance()->get('user')->getIsAdmin()) + { + throw new \Exception('You do not have permission to do that.'); + } + + $method = Registry::getInstance()->get('requestMethod'); + $user = $this->_userStore->getById($id); + + if($method == 'POST') + { + $values = $this->getParams(); + } + else + { + $values = $user->getDataArray(); + $values['admin'] = $values['is_admin']; + } + + $form = $this->userForm($values, 'edit/' . $id); + + if($method != 'POST' || ($method == 'POST' && !$form->validate())) + { + $view = new b8\View('UserForm'); + $view->type = 'edit'; + $view->user = $user; + $view->form = $form; + + return $view->render(); + } + + $values = $form->getValues(); + $values['is_admin'] = $values['admin'] ? 1 : 0; + + if(!empty($values['password'])) + { + $values['hash'] = password_hash($values['password'], PASSWORD_DEFAULT); + } + + $user->setValues($values); + $user = $this->_userStore->save($user); + + header('Location: /user'); + die; + } + + protected function userForm($values, $type = 'add') + { + $form = new Form(); + $form->setMethod('POST'); + $form->setAction('/user/' . $type); + $form->addField(new Form\Element\Csrf('csrf')); + + $field = new Form\Element\Email('email'); + $field->setRequired(true); + $field->setLabel('Email Address'); + $field->setClass('span4'); + $form->addField($field); + + $field = new Form\Element\Text('name'); + $field->setRequired(true); + $field->setLabel('Name'); + $field->setClass('span4'); + $form->addField($field); + + $field = new Form\Element\Password('password'); + $field->setRequired(true); + $field->setLabel('Password' . ($type == 'edit' ? ' (leave blank to keep current password)' : '')); + $field->setClass('span4'); + $form->addField($field); + + $field = new Form\Element\Checkbox('admin'); + $field->setRequired(false); + $field->setCheckedValue(1); + $field->setLabel('Is this user an administrator?'); + $form->addField($field); + + $field = new Form\Element\Submit(); + $field->setValue('Save User'); + $field->setClass('btn-success'); + $form->addField($field); + + $form->setValues($values); + return $form; + } + + public function delete($id) + { + if(!Registry::getInstance()->get('user')->getIsAdmin()) + { + throw new \Exception('You do not have permission to do that.'); + } + + $user = $this->_userStore->getById($id); + $this->_userStore->delete($user); + + header('Location: /user'); + } +} \ No newline at end of file diff --git a/PHPCI/Helper/User.php b/PHPCI/Helper/User.php new file mode 100644 index 00000000..93408025 --- /dev/null +++ b/PHPCI/Helper/User.php @@ -0,0 +1,12 @@ +get('user'); + return call_user_func_array(array($user, $method), $params); + } +} \ No newline at end of file diff --git a/PHPCI/Model/Base/UserBase.php b/PHPCI/Model/Base/UserBase.php new file mode 100644 index 00000000..6f4fd31f --- /dev/null +++ b/PHPCI/Model/Base/UserBase.php @@ -0,0 +1,210 @@ + null, + 'email' => null, + 'hash' => null, + 'is_admin' => null, + 'name' => null, + ); + protected $_getters = array( + 'id' => 'getId', + 'email' => 'getEmail', + 'hash' => 'getHash', + 'is_admin' => 'getIsAdmin', + 'name' => 'getName', + + + ); + + protected $_setters = array( + 'id' => 'setId', + 'email' => 'setEmail', + 'hash' => 'setHash', + 'is_admin' => 'setIsAdmin', + 'name' => 'setName', + + ); + public $columns = array( + 'id' => array( + 'type' => 'int', + 'length' => '11', + + 'primary_key' => true, + 'auto_increment' => true, + + ), + 'email' => array( + 'type' => 'varchar', + 'length' => '250', + + + + + ), + 'hash' => array( + 'type' => 'varchar', + 'length' => '250', + + + + + ), + 'is_admin' => array( + 'type' => 'tinyint', + 'length' => '1', + + + + + ), + 'name' => array( + 'type' => 'varchar', + 'length' => '250', + + + + + ), + ); + public $indexes = array( + 'PRIMARY' => array('unique' => true, 'columns' => 'id'), + 'idx_email' => array('unique' => true, 'columns' => 'email'), + ); + public $foreignKeys = array( + ); + + + + public function getId() + { + $rtn = $this->_data['id']; + + + return $rtn; + } + + public function getEmail() + { + $rtn = $this->_data['email']; + + + return $rtn; + } + + public function getHash() + { + $rtn = $this->_data['hash']; + + + return $rtn; + } + + public function getIsAdmin() + { + $rtn = $this->_data['is_admin']; + + + return $rtn; + } + + public function getName() + { + $rtn = $this->_data['name']; + + + return $rtn; + } + + + + public function setId($value) + { + $this->_validateNotNull('Id', $value); + $this->_validateInt('Id', $value); + if($this->_data['id'] == $value) + { + return; + } + + $this->_data['id'] = $value; + + $this->_setModified('id'); + } + + public function setEmail($value) + { + $this->_validateNotNull('Email', $value); + $this->_validateString('Email', $value); + if($this->_data['email'] == $value) + { + return; + } + + $this->_data['email'] = $value; + + $this->_setModified('email'); + } + + public function setHash($value) + { + $this->_validateNotNull('Hash', $value); + $this->_validateString('Hash', $value); + if($this->_data['hash'] == $value) + { + return; + } + + $this->_data['hash'] = $value; + + $this->_setModified('hash'); + } + + public function setIsAdmin($value) + { + $this->_validateNotNull('IsAdmin', $value); + $this->_validateInt('IsAdmin', $value); + if($this->_data['is_admin'] == $value) + { + return; + } + + $this->_data['is_admin'] = $value; + + $this->_setModified('is_admin'); + } + + public function setName($value) + { + $this->_validateNotNull('Name', $value); + $this->_validateString('Name', $value); + if($this->_data['name'] == $value) + { + return; + } + + $this->_data['name'] = $value; + + $this->_setModified('name'); + } + + + + + +} diff --git a/PHPCI/Model/User.php b/PHPCI/Model/User.php new file mode 100644 index 00000000..35d73b98 --- /dev/null +++ b/PHPCI/Model/User.php @@ -0,0 +1,20 @@ +getById($value, $useConnection); + } + + + + public function getById($value, $useConnection = 'read') + { + if(is_null($value)) + { + throw new \b8\Exception\HttpException('Value passed to ' . __FUNCTION__ . ' cannot be null.'); + } + + $stmt = \b8\Database::getConnection($useConnection)->prepare('SELECT * FROM user WHERE id = :id LIMIT 1'); + $stmt->bindValue(':id', $value); + + if($stmt->execute()) + { + if($data = $stmt->fetch(\PDO::FETCH_ASSOC)) + { + return new \PHPCI\Model\User($data); + } + } + + return null; + } + + public function getByEmail($value, $useConnection = 'read') + { + if(is_null($value)) + { + throw new \b8\Exception\HttpException('Value passed to ' . __FUNCTION__ . ' cannot be null.'); + } + + $stmt = \b8\Database::getConnection($useConnection)->prepare('SELECT * FROM user WHERE email = :email LIMIT 1'); + $stmt->bindValue(':email', $value); + + if($stmt->execute()) + { + if($data = $stmt->fetch(\PDO::FETCH_ASSOC)) + { + return new \PHPCI\Model\User($data); + } + } + + return null; + } + +} diff --git a/PHPCI/Store/UserStore.php b/PHPCI/Store/UserStore.php new file mode 100644 index 00000000..5d44ae92 --- /dev/null +++ b/PHPCI/Store/UserStore.php @@ -0,0 +1,20 @@ +
  • Rebuild
  • + User()->getIsAdmin()): ?>
  • Delete Build
  • - - + diff --git a/PHPCI/View/BuildsTable.phtml b/PHPCI/View/BuildsTable.phtml index 719b88bb..0a47b105 100644 --- a/PHPCI/View/BuildsTable.phtml +++ b/PHPCI/View/BuildsTable.phtml @@ -39,12 +39,14 @@ switch($build->getStatus())
    View + User()->getIsAdmin()): ?> +
    diff --git a/PHPCI/View/Index.phtml b/PHPCI/View/Index.phtml index 2e13c6f0..decd91bb 100644 --- a/PHPCI/View/Index.phtml +++ b/PHPCI/View/Index.phtml @@ -6,6 +6,7 @@
    diff --git a/PHPCI/View/Login.phtml b/PHPCI/View/Login.phtml new file mode 100644 index 00000000..62b655c1 --- /dev/null +++ b/PHPCI/View/Login.phtml @@ -0,0 +1,53 @@ + + + + + Log in + + + + + + + + + + + + +
    +
    +
    + +
    + + +
    +
    + + \ No newline at end of file diff --git a/PHPCI/View/Project.phtml b/PHPCI/View/Project.phtml index d5c85583..3f3bd58d 100644 --- a/PHPCI/View/Project.phtml +++ b/PHPCI/View/Project.phtml @@ -10,11 +10,12 @@
  • getTitle(); ?>
  • -
  • Edit Project
  • Build Now
  • + + User()->getIsAdmin()): ?> +
  • Edit Project
  • Delete Project
  • - - + diff --git a/PHPCI/View/User.phtml b/PHPCI/View/User.phtml new file mode 100644 index 00000000..32700af4 --- /dev/null +++ b/PHPCI/View/User.phtml @@ -0,0 +1,65 @@ +
    +

    Users

    +
    + +
    +
    +
    + +
    +
    +
    + + + + + + + + + + + + getIsAdmin()) + { + case 0: + $cls = ''; + $status = 'No'; + break; + + case 1: + $cls = 'warning'; + $status = 'Yes'; + break; + } + ?> + + + + + + + + +
    Email AddressNameAdministrator
    getEmail(); ?>getName(); ?> + User()->getIsAdmin()): ?> +
    + Edit + + +
    + +
    +
    +
    \ No newline at end of file diff --git a/PHPCI/View/UserForm.phtml b/PHPCI/View/UserForm.phtml new file mode 100644 index 00000000..9a11ba82 --- /dev/null +++ b/PHPCI/View/UserForm.phtml @@ -0,0 +1,18 @@ +
    +

    getName() ?>

    +
    + +
    +
    +
    + +

    Fill in the form to the right to add a new user.

    + +

    Edit user details using the form to the right.

    + +
    +
    +
    + +
    +
    \ No newline at end of file diff --git a/README.md b/README.md index ef3e685b..92e69aa6 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,8 @@ _**Please be aware that this is a brand new project, in an alpha state, so there * Multiple PHP-version tests. * Multiple testing workers. * Install PEAR or PECL extensions. +* Deployments. +* Success / Failure emails. ##Installing PHPCI: ####Pre-requisites: diff --git a/composer.json b/composer.json index 9840acbe..d4342720 100644 --- a/composer.json +++ b/composer.json @@ -16,6 +16,7 @@ "phpmd/phpmd" : "1.*", "sebastian/phpcpd": "1.*", "squizlabs/php_codesniffer": "1.*", + "ircmaxell/password-compat": "1.x", "ext-yaml": "*" } } \ No newline at end of file diff --git a/index.php b/index.php index ea60a89d..a3a036fe 100644 --- a/index.php +++ b/index.php @@ -3,6 +3,8 @@ error_reporting(E_ALL & ~E_NOTICE); ini_set('display_errors', 'on'); +session_start(); + require_once('bootstrap.php'); $fc = new PHPCI\Application(); diff --git a/install.php b/install.php index ac7ceb3f..0c2cb9c6 100644 --- a/install.php +++ b/install.php @@ -1,10 +1,12 @@ -#!/usr/bin/php generate(); - -print 'INSTALLING: Composer' . PHP_EOL; -file_put_contents('./composerinstaller.php', file_get_contents('https://getcomposer.org/installer')); -shell_exec('php ./composerinstaller.php'); -unlink('./composerinstaller.php'); +if(!file_exists('./composer.phar')) +{ + print 'INSTALLING: Composer' . PHP_EOL; + file_put_contents('./composerinstaller.php', file_get_contents('https://getcomposer.org/installer')); + shell_exec('php ./composerinstaller.php'); + unlink('./composerinstaller.php'); +} print 'RUNNING: Composer' . PHP_EOL; shell_exec('./composer.phar install'); -function ask($question) +require_once('bootstrap.php'); + +$gen = new b8\Database\Generator(b8\Database::getConnection(), 'PHPCI', './PHPCI/Model/Base/'); +$gen->generate(); + +$adminEmail = ask('Enter your email address: '); +$adminPass = ask('Enter your desired admin password: '); +$adminName = ask('Enter your name: '); + +try +{ + $user = new PHPCI\Model\User(); + $user->setEmail($adminEmail); + $user->setName($adminName); + $user->setIsAdmin(1); + $user->setHash(password_hash($adminPass, PASSWORD_DEFAULT)); + + $store = b8\Store\Factory::getStore('User'); + $store->save($user); + + print 'User account created!' . PHP_EOL; +} +catch(Exception $ex) +{ + print 'There was a problem creating your account. :(' . PHP_EOL; + print $ex->getMessage(); +} + + +function ask($question, $emptyOk = false) { print $question . ' '; @@ -42,5 +71,12 @@ function ask($question) $rtn = fgets($fp); fclose($fp); - return trim($rtn); + $rtn = trim($rtn); + + if(!$emptyOk && empty($rtn)) + { + $rtn = ask($question, $emptyOk); + } + + return $rtn; } \ No newline at end of file