From 011c9d8ed5653308c1e76a9ff4f8ed5fdd5e10a4 Mon Sep 17 00:00:00 2001 From: coderkun Date: Wed, 22 Jan 2014 16:31:25 +0100 Subject: [PATCH] integrate AuthComponent and implement CRUD for users --- agents/ToplevelAgent.inc | 2 + app/Controller.inc | 65 ++++++- configs/AppConfig.inc | 10 +- controllers/HtmlController.inc | 8 +- controllers/SeminariesController.inc | 9 + controllers/UsersController.inc | 160 ++++++++++++++++++ locale/de_DE/LC_MESSAGES/The Legend of Z.mo | Bin 744 -> 1407 bytes locale/de_DE/LC_MESSAGES/The Legend of Z.po | 75 +++++++- models/UsersModel.inc | 132 +++++++++++++++ views/html/html.tpl | 5 + views/html/menu/index.tpl | 5 + views/html/users/create.tpl | 14 ++ views/html/users/delete.tpl | 8 + views/html/users/edit.tpl | 14 ++ views/html/users/index.tpl | 3 + views/html/users/login.tpl | 12 ++ .../empty => views/html/users/logout.tpl | 0 views/html/users/user.tpl | 3 + 18 files changed, 509 insertions(+), 16 deletions(-) create mode 100644 views/html/users/create.tpl create mode 100644 views/html/users/delete.tpl create mode 100644 views/html/users/edit.tpl create mode 100644 views/html/users/login.tpl rename controllers/components/empty => views/html/users/logout.tpl (100%) diff --git a/agents/ToplevelAgent.inc b/agents/ToplevelAgent.inc index c545975a..eee33450 100644 --- a/agents/ToplevelAgent.inc +++ b/agents/ToplevelAgent.inc @@ -265,6 +265,7 @@ /** * Run the Controller of this Agent and its SubAgents. * + * @throws AccessDeniedException * @throws IdNotFoundException * @throws ServiceUnavailableException * @throws DatamodelException @@ -292,6 +293,7 @@ /** * Run IntermediateAgent. * + * @throws AccessDeniedException * @throws ParamsNotValidException * @throws IdNotFoundException * @throws ServiceUnavailableException diff --git a/app/Controller.inc b/app/Controller.inc index 4403407b..24301a88 100644 --- a/app/Controller.inc +++ b/app/Controller.inc @@ -19,12 +19,30 @@ */ abstract class Controller extends \nre\core\Controller { + /** + * Required components + * + * @var array + */ + public $components = array('auth'); + /** + * Required models + * + * @var array + */ + public $models = array('users'); /** * Linker instance * * @var Linker */ protected $linker = null; + /** + * Data of currently logged in user if any + * + * @var array + */ + protected static $user = null; @@ -58,13 +76,19 @@ { parent::preFilter($request, $response); + // Check rights + $this->checkPermission(); + // Create linker $this->linker = new \nre\core\Linker($this->request); + + // Set userdata + $this->set('loggedUser', static::$user); } /** - * Prefilter that is executed after running the Controller. + * Postfilter that is executed after running the Controller. * * @param Request $request Current request * @param Response $response Current response @@ -72,9 +96,44 @@ public function postFilter(\nre\core\Request $request, \nre\core\Response $response) { parent::postFilter($request, $response); + } + + + + + /** + * Check user permissions. + * + * @throws AccessDeniedException + */ + private function checkPermission() + { + // Determine user + try { + $userId = $this->Auth->getUserId(); + if(!is_null($userId)) { + static::$user = $this->Users->getUserById($this->Auth->getUserId()); + } + } + catch(\nre\exceptions\IdNotFoundException $e) { + } - // Set title - $this->set('title', $this->request->getParam(1, 'intermediate')); + + // Determine permissions + $action = $this->request->getParam(2, 'action'); + if(!property_exists($this, 'permissions')) { + return; // Allow if nothing is specified + } + if(!array_key_exists($action, $this->permissions)) { + return; // Allow if Action is not specified + } + $permissions = $this->permissions[$action]; + + + // Check permissions + if(is_null(static::$user)) { + throw new \nre\exceptions\AccessDeniedException(); + } } } diff --git a/configs/AppConfig.inc b/configs/AppConfig.inc index 3467c3be..c4ef5929 100644 --- a/configs/AppConfig.inc +++ b/configs/AppConfig.inc @@ -69,9 +69,10 @@ * @var array */ public static $routes = array( - array('css/?(.*)', 'css/$1?layout=stylesheet', false), - array('users/(.+)', 'users/user/$1', false), - array('seminaries/(.+)', 'seminaries/seminary/$1', false) + array('css/?(.*)', 'css/$1?layout=stylesheet', false), + array('users/([^/]+)/(edit|delete)', 'users/$2/$1', true), + array('users/(?!(index|login|logout|create|edit|delete))', 'users/user/$1', true), + array('seminaries/(.+)', 'seminaries/seminary/$1', false) ); @@ -82,7 +83,8 @@ * @var array */ public static $reverseRoutes = array( - array('users/user/(.*)', 'users/$1', false), + array('users/user/(.*)', 'users/$1', true), + array('users/([^/]+)/(.*)', 'users/$2/$1', true), array('seminaries/seminary/(.*)', 'seminaries/$1', false) ); diff --git a/controllers/HtmlController.inc b/controllers/HtmlController.inc index 3c33d732..a2659898 100644 --- a/controllers/HtmlController.inc +++ b/controllers/HtmlController.inc @@ -17,7 +17,7 @@ * * @author Oliver Hanraths */ - class HtmlController extends \nre\core\Controller + class HtmlController extends \hhu\z\Controller { @@ -36,9 +36,6 @@ // Set content-type $this->response->addHeader("Content-type: text/html; charset=utf-8"); - - // Start session - session_start(); } @@ -51,6 +48,9 @@ { // Set the name of the current IntermediateAgent as page title $this->set('title', $this->request->getParam(1, 'intermediate')); + + // Set userdata + $this->set('loggedUser', static::$user); } } diff --git a/controllers/SeminariesController.inc b/controllers/SeminariesController.inc index 4f026f41..4a9581bf 100644 --- a/controllers/SeminariesController.inc +++ b/controllers/SeminariesController.inc @@ -25,6 +25,15 @@ * @var array */ public $models = array('seminaries', 'users'); + /** + * User permissions + * + * @var array + */ + public $permissions = array( + 'index' => array(), + 'seminary' => array() + ); diff --git a/controllers/UsersController.inc b/controllers/UsersController.inc index 0a3ce645..aea5ffbb 100644 --- a/controllers/UsersController.inc +++ b/controllers/UsersController.inc @@ -19,6 +19,19 @@ */ class UsersController extends \hhu\z\Controller { + /** + * User permissions + * + * @var array + */ + public $permissions = array( + 'index' => array(), + 'user' => array(), + 'create' => array(), + 'edit' => array(), + 'delete' => array() + ); + @@ -56,6 +69,153 @@ } + /** + * Action: login. + * + * Log in a user. + */ + public function login() + { + $username = ''; + + // Log the user in + if($this->request->getRequestMethod() == 'POST' && !empty($this->request->getPostParam('login'))) + { + $username = $this->request->getPostParam('username'); + $userId = $this->Users->login( + $username, + $this->request->getPostParam('password') + ); + + if(!is_null($userId)) + { + $this->Auth->setUserId($userId); + $user = $this->Users->getUserById($userId); + + $this->redirect($this->linker->link(array($user['url']), 1)); + } + } + + + // Pass data to view + $this->set('username', $username); + $this->set('failed', ($this->request->getRequestMethod() == 'POST')); + } + + + /** + * Action: logout. + * + * Log out a user. + */ + public function logout() + { + // Unset the currently logged in user + $this->Auth->setUserId(null); + + // Redirect + $this->redirect($this->linker->link(array())); + } + + + /** + * Action: create. + * + * Create a new user. + */ + public function create() + { + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('create'))) + { + // Create new user + $userId = $this->Users->createUser( + $this->request->getPostParam('username'), + $this->request->getPostParam('email'), + $this->request->getPostParam('password') + ); + + // Redirect to user + $user = $this->Users->getUserById($userId); + $this->redirect($this->linker->link(array($user['url']), 1)); + } + } + + + /** + * Action: edit. + * + * Edit a user. + * + * @throws IdNotFoundException + * @param string $userUrl URL-Username of an user + */ + public function edit($userUrl) + { + // User + $user = $this->Users->getUserByUrl($userUrl); + + // Check request method + if($this->request->getRequestMethod() == 'POST') + { + // Save changes + if(!empty($this->request->getPostParam('save'))) + { + // Edit user + $this->Users->editUser( + $user['id'], + $this->request->getPostParam('username'), + $this->request->getPostParam('email'), + $this->request->getPostParam('password') + ); + } + + + // Redirect to user + $this->redirect($this->linker->link(array($user['url']), 1)); + } + + + // Pass data to view + $this->set('user', $user); + } + + + /** + * Action: delete. + * + * Delete a user. + * + * @throws IdNotFoundException + * @param string $userUrl URL-Username of an user + */ + public function delete($userUrl) + { + // User + $user = $this->Users->getUserByUrl($userUrl); + + // Check request method + if($this->request->getRequestMethod() == 'POST') + { + // Check confirmation + if($this->request->getPostParam('delete') == 'delete') + { + // Delete user + $this->Users->deleteUser($user['id']); + + // Redirect to overview + $this->redirect($this->linker->link(null, 1)); + } + + // Redirect to entry + $this->redirect($this->linker->link(array('user', $user['url']), 1)); + } + + + // Show confirmation + $this->set('user', $user); + } + + } ?> diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.mo b/locale/de_DE/LC_MESSAGES/The Legend of Z.mo index ec51389a69bdbf4ffb9b427614d53912f88b5c08..a8dd7d2c500d170581bcd7b63409bb2c2e4438c8 100644 GIT binary patch literal 1407 zcmca7#4?qEfq_AUfq_AWfq`KU0|SFB0|P@0BS@5i;Q}KA13v=;!$n3025trhhKGy{ z44e!M3~w137+4t?7``(yFbFU(F#Kg?VBle3VBlkdh)XjuFt9N&FeoxHFmNz1Flaz& zLna0WE(Qh$D<%d80|o{LSEzUyR9`(*Un`XEVPaqqW?*2L4COCpVqoB9U|?9s#K170 zk%3_k69a<;$h|BK41x>{45cg(_3bPW`)9L2+_#7Y;{MGn5clne${%H6VBlk5U^v49 zap!HQ{1X-i1}g>zhF2^Qf2p!U#&V#pU@$DGb4>xtV#1MVYC^48a-sr8y}I zB^eNV6dpCqQ7wMdFjqlCwOCW3C^a!Br&1v)RUrlJmK1x2(Bjmhyu{p8Fr%0uIWaFe zHHRS?>~tuVqL5UnpjxbupQoT&%mC5JP?VaUSzMA@l$xTDpT`hTl%JKFT%zllq8pZ4 zRGgWgXQdF5k*eU6nx2}MqL81a5XBYXAEFBmlFa-(U6;g?R4WA|149#C14CUSBLzb< zD$Gh7={{73U~HL)luHM1l&kHIZ9BPSIlk%H1_DuZig zUfSV38AYXe>F8;+G_?qB9|JU{mN0mi78R#51n1}ED5M}MPI!d zW^#r?dMdf)2%{cE-3Xv%uCIK Rrr)B>)S?oF;?&F%1^~m@V7LGP delta 374 zcmey*^@6qjo)F7a1_lNOb_NCp83qQ15C#SYZUzR19*`gd1H%pm1_o9J28Nvs3=BLB z3=I1j7#O%17#Pk##ji6kFbFd+Fx+KeVBlq7VED+uz%ZYYf#E*`0|Og~%>*%T856{u zO-u|793XR<7#Ktu80r}gGBGd+FfcG&XJTMrWME)mVPIfjhFT7yK$e4OkOM(96VzY^ z*P^2QA_mXAlA`>S(&UoN{5*!>)ZEOx#G=g9VusM-)S_aBdb->query('SELECT id, password FROM users WHERE username = ?', 's', $username); + if(!empty($data)) + { + $data = $data[0]; + if($this->verify($password, $data['password'])) { + return $data['id']; + } + } + + + return null; + } + + + /** + * Create a new user. + * + * @param string $username Username of the user to create + * @param string $email E‑Mail-Address of the user to create + * @param string $password Password of the user to create + * @return int ID of the newly created user + */ + public function createUser($username, $email, $password) + { + $this->db->query( + 'INSERT INTO users '. + '(username, url, email, password) '. + 'VALUES '. + '(?, ?, ?, ?)', + 'ssss', + $username, + \nre\core\Linker::createLinkParam($username), + $email, + $this->hash($password) + ); + + + return $this->db->getInsertId(); + } + + + /** + * Edit a user. + * + * @throws DatamodelException + * @param string $username New name of user + * @param string $email Changed e‑mail-address of user + * @param string $password Changed plaintext password of user + */ + public function editUser($userId, $username, $email, $password) + { + try { + // Update user data + $this->db->query( + 'UPDATE users '. + 'SET username = ?, email = ? '. + 'WHERE id = ?', + 'ssi', + $sername, $email, + $userId + ); + + // Set new password + if(!empty($password)) + { + $this->db->query( + 'UPDATE users '. + 'SET password = ? '. + 'WHERE id = ?', + 'si', + $this->hash($password), + $userId + ); + } + } + catch(Exception $e) { + $this->db->rollback(); + throw $e; + } + finally { + $this->db->setAutocommit(true); + } + } + + + /** + * Delete a user. + * + * @param int $userId ID of the user to delete + */ + public function deleteUser($userId) + { + $this->db->query('DELETE FROM users WHERE id = ?', 'i', $userId); + } + + + + + /** + * Hash a password. + * + * @param string $password Plaintext password + * @return string Hashed password + */ + private function hash($password) + { + return password_hash($password, PASSWORD_DEFAULT); + } + + + /** + * Verify a password. + * + * @param string $password Plaintext password to verify + * @param string $hash Hashed password to match with + * @return boolean Verified + */ + private function verify($password, $hash) + { + return password_verify($password, $hash); + } + } ?> diff --git a/views/html/html.tpl b/views/html/html.tpl index 0f46493c..a9f5c842 100644 --- a/views/html/html.tpl +++ b/views/html/html.tpl @@ -14,6 +14,11 @@ + +
+ +
+
diff --git a/views/html/menu/index.tpl b/views/html/menu/index.tpl index ad0fbe39..f9f8246c 100644 --- a/views/html/menu/index.tpl +++ b/views/html/menu/index.tpl @@ -1,4 +1,9 @@
  • ">
  • ">
  • + +
  • + +
  • +
    diff --git a/views/html/users/create.tpl b/views/html/users/create.tpl new file mode 100644 index 00000000..caffac41 --- /dev/null +++ b/views/html/users/create.tpl @@ -0,0 +1,14 @@ +

    +

    + +
    + + +
    + +
    + +
    + + + diff --git a/views/html/users/delete.tpl b/views/html/users/delete.tpl new file mode 100644 index 00000000..10f280ab --- /dev/null +++ b/views/html/users/delete.tpl @@ -0,0 +1,8 @@ +

    +

    + + +
    + + +
    diff --git a/views/html/users/edit.tpl b/views/html/users/edit.tpl new file mode 100644 index 00000000..d3d1c2e0 --- /dev/null +++ b/views/html/users/edit.tpl @@ -0,0 +1,14 @@ +

    +

    + +
    + + +
    + +
    + +
    + + + diff --git a/views/html/users/index.tpl b/views/html/users/index.tpl index 3b94e6a0..9a5b525e 100644 --- a/views/html/users/index.tpl +++ b/views/html/users/index.tpl @@ -1,4 +1,7 @@

    +
    • diff --git a/views/html/users/login.tpl b/views/html/users/login.tpl new file mode 100644 index 00000000..36c13da4 --- /dev/null +++ b/views/html/users/login.tpl @@ -0,0 +1,12 @@ +

      +

      + +
      +
      + +
      + +
      +
      + +
      diff --git a/controllers/components/empty b/views/html/users/logout.tpl similarity index 100% rename from controllers/components/empty rename to views/html/users/logout.tpl diff --git a/views/html/users/user.tpl b/views/html/users/user.tpl index 91591dd3..1e8466b5 100644 --- a/views/html/users/user.tpl +++ b/views/html/users/user.tpl @@ -1,5 +1,8 @@

      +