Adding user accounts.

This commit is contained in:
Dan Cryer 2013-05-10 16:25:51 +01:00
parent a07ba4020c
commit 188162f0a2
22 changed files with 803 additions and 19 deletions

View file

@ -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;
}
}

View file

@ -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);

View file

@ -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);

View file

@ -0,0 +1,59 @@
<?php
namespace PHPCI\Controller;
use b8;
class SessionController extends b8\Controller
{
public function init()
{
$this->_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 &raquo;');
$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;
}
}

View file

@ -0,0 +1,167 @@
<?php
namespace PHPCI\Controller;
use b8,
b8\Registry,
PHPCI\Model\User,
b8\Form;
class UserController extends b8\Controller
{
public function init()
{
$this->_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');
}
}

12
PHPCI/Helper/User.php Normal file
View file

@ -0,0 +1,12 @@
<?php
namespace PHPCI\Helper;
class User
{
public function __call($method, $params = array())
{
$user = \b8\Registry::getInstance()->get('user');
return call_user_func_array(array($user, $method), $params);
}
}

View file

@ -0,0 +1,210 @@
<?php
/**
* User base model for table: user
*/
namespace PHPCI\Model\Base;
use b8\Model;
/**
* User Base Model
*/
class UserBase extends Model
{
public static $sleepable= array();
protected $_tableName = 'user';
protected $_modelName = 'User';
protected $_data = array(
'id' => 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');
}
}

20
PHPCI/Model/User.php Normal file
View file

@ -0,0 +1,20 @@
<?php
/**
* User model for table: user
*/
namespace PHPCI\Model;
require_once(APPLICATION_PATH . 'PHPCI/Model/Base/UserBase.php');
use PHPCI\Model\Base\UserBase;
/**
* User Model
* @uses PHPCI\Model\Base\UserBase
*/
class User extends UserBase
{
// This class has been left blank so that you can modify it - changes in this file will not be overwritten.
}

View file

@ -0,0 +1,68 @@
<?php
/**
* User base store for table: user
*/
namespace PHPCI\Store\Base;
use b8\Store;
/**
* User Base Store
*/
class UserStoreBase extends Store
{
protected $_tableName = 'user';
protected $_modelName = '\PHPCI\Model\User';
protected $_primaryKey = 'id';
public function getByPrimaryKey($value, $useConnection = 'read')
{
return $this->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;
}
}

20
PHPCI/Store/UserStore.php Normal file
View file

@ -0,0 +1,20 @@
<?php
/**
* User store for table: user
*/
namespace PHPCI\Store;
require_once(APPLICATION_PATH . 'PHPCI/Store/Base/UserStoreBase.php');
use PHPCI\Store\Base\UserStoreBase;
/**
* User Store
* @uses PHPCI\Store\Base\UserStoreBase
*/
class UserStore extends UserStoreBase
{
// This class has been left blank so that you can modify it - changes in this file will not be overwritten.
}

View file

@ -12,9 +12,9 @@
<li class="divider"></li>
<li class="nav-header">Options</li>
<li><a href="/build/rebuild/<?= $build->getId(); ?>"><i class="icon-cog"></i> Rebuild</a></li>
<?php if($this->User()->getIsAdmin()): ?>
<li><a href="javascript:confirmDelete('/build/delete/<?= $build->getId(); ?>')"><i class="icon-trash"></i> Delete Build</a></li>
<?php endif; ?>
</ul>
</div>
</div>

View file

@ -39,12 +39,14 @@ switch($build->getStatus())
<td>
<div class="btn-group">
<a class="btn" href="/build/view/<?= $build->getId(); ?>">View</a>
<?php if($this->User()->getIsAdmin()): ?>
<button class="btn dropdown-toggle" data-toggle="dropdown">
<span class="caret"></span>
</button>
<ul class="dropdown-menu">
<li><a href="javascript:confirmDelete('/build/delete/<?= $build->getId(); ?>');">Delete Build</a></li>
</ul>
<?php endif; ?>
</div>
</td>
</tr>

View file

@ -6,6 +6,7 @@
<div class="well" style="padding: 8px 0">
<ul class="nav nav-list">
<li><a href="/"><i class="icon-home"></i> Dashboard</a></li>
<li><a href="/user"><i class="icon-user"></i> Users</a></li>
<li class="divider"></li>
<li class="nav-header">Projects</li>

View file

@ -77,8 +77,9 @@
<div class="navbar-inner">
<div class="container">
<a class="brand" href="/">PHPCI <span class="badge badge-important" style="position: relative; top: -3px; margin-left: 10px">1.0 Alpha</span></a>
<?php if($this->User()->getIsAdmin()): ?>
<a class="pull-right btn" href="/project/add">Add Project</a>
<?php endif; ?>
</div>
</div>
</div>

53
PHPCI/View/Login.phtml Normal file
View file

@ -0,0 +1,53 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Log in</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" type="text/css" href="/assets/css/bootstrap.min.css">
<!-- link rel="stylesheet" type="text/css" href="/assets/css/cms.css" -->
<script src="http://code.jquery.com/jquery-1.8.1.min.js"></script>
<script src="/assets/js/bootstrap.min.js"></script>
<style type="text/css">
body
{
background: #246;
font-family: Roboto, Arial, Sans-Serif;
font-style: normal;
font-weight: 300;
padding-top: 70px;
}
#login-box
{
background: #fff;
border: 10px solid #369;
margin: 0 auto;
padding: 10px;
text-align: left;
width: 270px;
}
#logo {
display: block;
}
</style>
</head>
<body>
<div class="container">
<div class="row" style="margin-top: 10%; text-align: center">
<div class="" id="login-box">
<?= $form; ?>
</div>
<a id="logo" href="http://www.block8.co.uk/"><img src="http://cms.block8.co.uk/assets/img/small-logo-trans-white.png"></a>
</div>
</div>
</body>
</html>

View file

@ -10,11 +10,12 @@
<li><a href="/project/view/<?= $project->getId(); ?>"><i class="icon-folder-open"></i> <?= $project->getTitle(); ?></a></li>
<li class="divider"></li>
<li class="nav-header">Options</li>
<li><a href="/project/edit/<?= $project->getId(); ?>"><i class="icon-edit"></i> Edit Project</a></li>
<li><a href="/project/build/<?= $project->getId(); ?>"><i class="icon-cog"></i> Build Now</a></li>
<?php if($this->User()->getIsAdmin()): ?>
<li><a href="/project/edit/<?= $project->getId(); ?>"><i class="icon-edit"></i> Edit Project</a></li>
<li><a href="javascript:confirmDelete('/project/delete/<?= $project->getId(); ?>')"><i class="icon-trash"></i> Delete Project</a></li>
<?php endif; ?>
</ul>
</div>

65
PHPCI/View/User.phtml Normal file
View file

@ -0,0 +1,65 @@
<div id="title">
<h1>Users</h1>
</div>
<div class="row">
<div class="span3">
<div class="well" style="padding: 8px 0">
<ul class="nav nav-list">
<li><a href="/"><i class="icon-home"></i> Dashboard</a></li>
<li><a href="/user"><i class="icon-user"></i> Users</a></li>
<?php if($this->User()->getIsAdmin()): ?>
<li><a href="/user/add"><i class="icon-plus-sign"></i> Add User</a></li>
<?php endif; ?>
</ul>
</div>
</div>
<div class="span9">
<table class="table table-striped table-bordered">
<thead>
<tr>
<th>Email Address</th>
<th>Name</th>
<th>Administrator</th>
<th style="width: 1%"></th>
</tr>
</thead>
<tbody>
<?php foreach($users['items'] as $user): ?>
<?php
switch($user->getIsAdmin())
{
case 0:
$cls = '';
$status = 'No';
break;
case 1:
$cls = 'warning';
$status = 'Yes';
break;
}
?>
<tr class="<?= $cls; ?>">
<td><a href="/user/edit/<?= $user->getId(); ?>"><?= $user->getEmail(); ?></a></td>
<td><?= $user->getName(); ?></td>
<td><?= $status; ?></td>
<td>
<?php if($this->User()->getIsAdmin()): ?>
<div class="btn-group">
<a class="btn" href="/user/edit/<?= $user->getId(); ?>">Edit</a>
<button class="btn dropdown-toggle" data-toggle="dropdown">
<span class="caret"></span>
</button>
<ul class="dropdown-menu">
<li><a href="javascript:confirmDelete('/user/delete/<?= $user->getId(); ?>');">Delete User</a></li>
</ul>
</div>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>

18
PHPCI/View/UserForm.phtml Normal file
View file

@ -0,0 +1,18 @@
<div id="title">
<h1><?= $type == 'add' ? 'Add User' : 'Edit ' . $user->getName() ?></h1>
</div>
<div class="row">
<div class="span4">
<div class="well" style="">
<?php if($type == 'add'): ?>
<p>Fill in the form to the right to add a new user.</p>
<?php else: ?>
<p>Edit user details using the form to the right.</p>
<?php endif; ?>
</div>
</div>
<div class="span8">
<?= $form; ?>
</div>
</div>

View file

@ -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:

View file

@ -16,6 +16,7 @@
"phpmd/phpmd" : "1.*",
"sebastian/phpcpd": "1.*",
"squizlabs/php_codesniffer": "1.*",
"ircmaxell/password-compat": "1.x",
"ext-yaml": "*"
}
}

View file

@ -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();

View file

@ -1,10 +1,12 @@
#!/usr/bin/php
<?php
ini_set('display_errors', 'on');
error_reporting(E_ALL);
$dbHost = ask('Enter your MySQL host: ');
$dbName = ask('Enter the database name PHPCI should use: ');
$dbUser = ask('Enter your MySQL username: ');
$dbPass = ask('Enter your MySQL password: ');
$dbPass = ask('Enter your MySQL password: ', true);
$cmd = 'mysql -u' . $dbUser . (!empty($dbPass) ? ' -p' . $dbPass : '') . ' -h' . $dbHost . ' -e "CREATE DATABASE IF NOT EXISTS ' . $dbName . '"';
shell_exec($cmd);
@ -19,21 +21,48 @@ b8\Database::setReadServers(array('{$dbHost}'));
");
require_once('bootstrap.php');
$gen = new b8\Database\Generator(b8\Database::getConnection(), 'PHPCI', './PHPCI/Model/Base/');
$gen->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;
}