I'm using Zend Framework 2 with ZfcUser, BjyAuthorize and Doctrine for the database. Registration etc. works very well so far. My problem is, that registered users have no role assigned, so i want to add the role "user" to the user during registration.
I think i could attach this to the "register" event, but i don't know how to do that.
I hope someone can help me ...
(i used this tutorial for setting up zfcuser etc. http://samminds.com/2013/03/zfcuser-bjyauthorize-and-doctrine-working-together/)
public function onBootstrap(MvcEvent $e)
{
$zfcServiceEvents = $e->getApplication()->getServiceManager()->get('zfcuser_user_service')->getEventManager();
$zfcServiceEvents->attach('register', function($e) {
$user = $e->getParam('user');
// probably the role must be added here, with $user->addRole();
// but how do i get the user Role Entity to add from DB?
});
Building on DangelZM's answer, and using another reference (see link at end of my post) about the Event Manager, I came up with this solution which organizes the potential ZfcUser event listeners out into a user listener object.
Note: I created my own user module called NvUser, so depending on the name of your module you'll have to replace all references of NvUser to your user module name.
Summary
I created an NvUserListener object that can itself attach event listeners to the shared event manager, and house the event listener callbacks.
Inside NvUser/Module.php:
<?php
namespace NvUser;
use Zend\Mvc\MvcEvent;
use NvUser\Listener\NvUserListener;
class Module
{
public function onBootstrap(MvcEvent $mvcEvent)
{
$em = $mvcEvent->getApplication()->getEventManager();
$em->attach(new NvUserListener());
}
}
Inside NvUser/src/NvUser/Listener/NvUserListener.php:
<?php
namespace NvUser\Listener;
use Zend\EventManager\AbstractListenerAggregate;
use Zend\EventManager\EventManagerInterface;
use Zend\EventManager\Event;
class NvUserListener extends AbstractListenerAggregate
{
public function attach(EventManagerInterface $events)
{
$sharedManager = $events->getSharedManager();
$this->listeners[] = $sharedManager->attach('ZfcUser\Service\User', 'register', array($this, 'onRegister'));
$this->listeners[] = $sharedManager->attach('ZfcUser\Service\User', 'register.post', array($this, 'onRegisterPost'));
}
public function onRegister(Event $e)
{
$sm = $e->getTarget()->getServiceManager();
$em = $sm->get('doctrine.entitymanager.orm_default');
$user = $e->getParam('user');
$config = $sm->get('config');
$criteria = array('roleId' => $config['zfcuser']['new_user_default_role']);
$defaultUserRole = $em->getRepository('NvUser\Entity\Role')->findOneBy($criteria);
if ($defaultUserRole !== null)
{
$user->addRole($defaultUserRole);
}
}
public function onRegisterPost(Event $e)
{
$user = $e->getParam('user');
$form = $e->getParam('form');
// Do something after user has registered
}
}
Inside NvUser/config/module.config.php:
<?php
namespace NvUser;
return array(
'zfcuser' => array(
'new_user_default_role' => 'user',
),
);
References:
Understanding the Zend Framework 2 Event Manager
Maybe it's not the best solution, but it works for me.
Add user_role_id option in config scope.
public function onBootstrap(MvcEvent $mvcEvent)
{
$zfcServiceEvents = $mvcEvent->getApplication()->getServiceManager()->get('zfcuser_user_service')->getEventManager();
$zfcServiceEvents->attach('register', function($e) use($mvcEvent) {
$user = $e->getParam('user');
$em = $mvcEvent->getApplication()->getServiceManager()->get('doctrine.entitymanager.orm_default');
$config = $mvcEvent->getApplication()->getServiceManager()->get('config');
$defaultUserRole = $em->getRepository('SamUser\Entity\Role')->find($config['user_role_id']);
$user->addRole($defaultUserRole);
});
}
Maybe someone will offer better solution.
This work too.
public function onBootstrap(MvcEvent $mvcEvent)
{
$zfcServiceEvents = $mvcEvent->getApplication()-getServiceManager()->get('zfcuser_user_service')->getEventManager();
$zfcServiceEvents->attach('register', function($e) use($mvcEvent) {
$user = $e->getParam('user');
$em = $mvcEvent->getApplication()->getServiceManager()-get('doctrine.entitymanager.orm_default');
$defaultUserRole = $em->getRepository('SamUser\Entity\Role')-find('id_of_your_role_on_table_role_for_example: '2'');
$user->addRole($defaultUserRole);
});
}
I just used a MySQL trigger
DROP TRIGGER IF EXISTS `user_role_after_insert_trig`;
DELIMITER //
CREATE TRIGGER `user_role_after_insert_trig` AFTER INSERT ON `user`
FOR EACH ROW begin
insert into user_role_linker (user_id,role_id) values (new.user_id, 5);
end
//
DELIMITER ;
Related
I have just uploaded my app into a shared hosting environment and it does not seem to be working properly.
I have 2 plugins registered. One checks for session timeout and the other check for session is created after logged in.
the pproblem is that after the second plugin(security.php) kicks in it suppose to redirect the user to the login screen because session has not been created yet. Upon redirection the page displays :The page isn't redirecting properly.
I am not sure what is happenning since everything works fine locally.Below are my two files i mentioned here.
Security.php(here you can see that i have tried couple options, but nothing worked).
class Plugins_security extends Zend_Controller_Plugin_Abstract
{
public function preDispatch (Zend_Controller_Request_Abstract $request)
{
$auth = Zend_Auth::getInstance();
$moduleName = $request->getModuleName();
//$vc = new Zend_Application_Resource_View();
if ($request->getModuleName() != "auth")
{
$auth = Zend_Auth::getInstance();
if (! $auth->hasIdentity())
{
//$redirector = Zend_Controller_Action_HelperBroker::getStaticHelper(
//'redirector');
$flashMessenger = Zend_Controller_Action_HelperBroker::getStaticHelper('FlashMessenger');
$flashMessenger->addMessage(array('message' => 'Sua sessão expirou. Favor logar novamente', 'status' => 'info'));
//$this->_redirect('/auth/login/',array(‘code’ => 301));
$r = Zend_Controller_Action_HelperBroker::getStaticHelper('redirector');
$r->gotoSimple("index", "login", "auth");
//header('Location: /auth/login/');
//return;
}
}
}
}
timeout.php
class Plugins_timeout extends Zend_Controller_Plugin_Abstract
{
protected $_auth = null;
protected $_acl = null;
protected $_flashMessenger = null;
protected static $_ZEND_SESSION_NAMESPACE_EXPIRATION_SECONDS= 900;
public function preDispatch(Zend_Controller_Request_Abstract $request)
{
Zend_Session::start();
$moduleName = parent::getRequest()->getModuleName();
if($moduleName !='auth'){
if (isset($_SESSION['LAST_ACTIVITY']) && (time() - $_SESSION['LAST_ACTIVITY'] > self::$_ZEND_SESSION_NAMESPACE_EXPIRATION_SECONDS)) {
// last request was more than 30 minates ago
session_destroy(); // destroy session data in storage
session_unset(); // unset $_SESSION variable for the runtime
$front = Zend_Controller_Front::getInstance();
$_baseUrl=$front->getBaseUrl();
Zend_Debug::dump(time() - $_SESSION['LAST_ACTIVITY']);
header("Location:$_baseUrl/auth/login/index/timeout/1" );
}else{
$_SESSION['LAST_ACTIVITY']= time();
}
}
}
}
Any help is appreciated. I need to deploy this app ASAP.
thank you.
I think you want:
$r = Zend_Controller_Action_HelperBroker::getStaticHelper('redirector');
$r->gotoSimpleAndExit("index", "login", "auth"); // Note the 'AndExit' suffix.
$r->gotoXXX() just sets the correct header and codes in the $response object, but allows the rest of the dispatch to continue. In contrast, the AndExit part immediately sends the response to the client and exits.
[Not clear why AndExit would not be required in your local environment, though...]
I ll reframe my question based on some research I have done?
I need to store lot of errors separately like $_SESSION['client_error'],$_SESSION['state_error'] etc.
According to zend documentation do I have to store it like this for each error?
$client_error = new Zend_Session_Namespace(''client_error);
$state_error = new Zend_Session_Namespace('state_erro'); and so on?
This is my code in the controller.
I am storing it as
$this->view->state_error_message=$state_error;
After I echo $this->state_error in the view I want to unset it.
Ok here are few more things I tried:
In the controller in policyInfoAction:
session_start();
$error_message = new Zend_Session_Namespace('error_message');
$error_message="TEST";
$this->view->error_message=$error_message;
$this->_redirect('/pdp/client-info/');
In the view in client-info:
session_start();
<?php echo $this->error_message; ?>
This returns nothing.
Ok this is my updated code:
public function clientInfoAction()
{
$errors = new Zend_Session_Namespace('errors');
// get the error arrays
$client_errors = (isset($errors->client_error)) ? $errors->client_error : array();
$state_errors = (isset($errors->state_error)) ? $errors->state_error : array();
unset($errors->client_error, $errors->state_error); // delete from the session
// assign the values to the view
$this->view->client_errors = $client_errors;
$this->view->state_errors = $state_errors;
}
public function policyInfoAction()
{
if (count($arrErrors) > 0)
{
// The error array had something in it. There was an error.
$strError="";
foreach ($arrErrors as $error)
{
$strError="";
$errors->client_error = array();
$errors->state_error = array();
foreach ($arrErrors as $error)
{
$strError .= $error;
// to add errors to each type:
$errors->client_error['client_error'] = $strError;
$errors->client_error[] = $strError;
$this->_redirect('/pdp/client-info/');
}
}
}
When i echo $this->client_errors I get 'Array'
Here is some advice and suggestions that can hopefully get you on the right track.
First, when using Zend_Session and/or Zend_Session_Namespace, you never want to use PHP's session_start() function1. If you start a session with session_start(), and then try to use Zend_Session, it will throw an exception that another session already exists.
Therefore, remove all session_start() calls from your Zend Framework application.
Second, you mentioned you had a lot of messages you need to store, so this may not be the right thing for you, but see the FlashMessenger action helper. This allows you to set a message in a controller, and then access it on the next page request. The messages only live for one page hop, so after the next page load, they are deleted. You can store many messages with the FlashMessenger, but your access to them is not very controlled. You could use multiple flash messengers each in differen namespaces also.
To solve your problem in particular, you could just do something like this:
// in controller that is validating
$errors = new Zend_Session_Namespace('errors');
$errors->client_error = array();
$errors->state_error = array();
// to add errors to each type:
$errors->client_error['some_error'] = 'You had some error, please try again.';
$errors->client_error['other_error'] = 'Other error occurred.';
$errors->client_error[] = 'Other error, not using a named key';
$errors->state_error[] = MY_STATE_PARSING_0;
What is happening here is we are getting a session namespace called errors creating new properties for client_error and state_error that are both arrays. You don't technically have to use multiple Zend_Session_Namespaces.
Then to clear the messages on the next page load, you can do this:
// from controller again, on the next page load
$errors = new Zend_Session_Namespace('errors');
// get the error arrays
$client_errors = (isset($errors->client_error)) ? $errors->client_error : array();
$state_errors = (isset($errors->state_error)) ? $errors->state_error : array();
unset($errors->client_error, $errors->state_error); // delete from the session
// assign the values to the view
$this->view->client_errors = $client_errors;
$this->view->state_errors = $state_errors;
See also the source code for Zend_Controller_Action_Helper_FlashMessenger which can give you some idea on managing data in session namespaces.
I don't know if this will help you or not but here is the code for a controller that just takes an id from a form a gathers data based on that id an assigns that data to the session (to be used throughout the module) and then unsets that data when appropriate. and Never leaves the Index page.
<?php
class Admin_IndexController extends Zend_Controller_Action
{
//zend_session_namespace('location')
protected $_session;
/**
*set the layout from default to admin for this controller
*/
public function preDispatch() {
$this->_helper->layout->setLayout('admin');
}
/**
*initiaize the flashmessenger and assign the _session property
*/
public function init() {
if ($this->_helper->FlashMessenger->hasMessages()) {
$this->view->messages = $this->_helper->FlashMessenger->getMessages();
}
//set the session namespace to property for easier access
$this->_session = new Zend_Session_Namespace('location');
}
/**
*Set the Station and gather data to be set in the session namespace for use
* in the rest of the module
*/
public function indexAction() {
//get form and pass to view
$form = new Admin_Form_Station();
$form->setAction('/admin/index');
$form->setName('setStation');
$this->view->station = $this->_session->stationName;
$this->view->stationComment = $this->_session->stationComment;
$this->view->form = $form;
try {
//get form values from request object
if ($this->getRequest()->isPost()) {
if ($form->isValid($this->getRequest()->getPost())) {
$data = (object)$form->getValues();
//set session variable 'station'
$this->_session->station = $data->station;
$station = new Application_Model_DbTable_Station();
$currentStation = $station->getStation($this->_session->station);
$this->_session->stationName = $currentStation->station;
$this->_session->stationComment = $currentStation->comment;
//assign array() of stations to session namespace
$stations = $station->fetchAllStation();
$this->_session->stations = $stations;
//assign array() of bidlocations to session namespace
$bidLocation = new Application_Model_DbTable_BidLocation();
$bidLocations = $bidLocation->fetchAllBidLocation($this->_stationId);
$this->_session->bidLocations = $bidLocations;
$this->_redirect($this->getRequest()->getRequestUri());
}
}
} catch (Zend_Exception $e) {
$this->_helper->flashMessenger->addMessage($e->getMessage());
$this->_redirect($this->getRequest()->getRequestUri());
}
}
/**
*Unset Session values and redirect to the index action
*/
public function changestationAction() {
Zend_Session::namespaceGet('location');
Zend_Session::namespaceUnset('location');
$this->getHelper('Redirector')->gotoSimple('index');
}
}
just to be complete i start the session in the bootstrap. On the theory that if I need it great if not no harm.
protected function _initsession() {
//start session
Zend_Session::start();
}
this is all the view is:
<?php if (!$this->station): ?>
<div class="span-5 prepend-2">
<?php echo $this->form ?>
</div>
<div class="span-10 prepend-2 last">
<p style="font-size: 2em">Please select the Station you wish to perform Administration actions on.</p>
</div>
<?php else: ?>
<div class="span-19 last">
<?php echo $this->render('_station.phtml') ?>
</div>
<?php endif; ?>
According to Randomness will get you everywhere.
Ryan’s Blog
the action stack component of Zend Framework is un-needed and that a partial view can be combined with Zend_Acl and Zend_Auth for the purpose of authenticating and controlling resources.
I have not been able to find any suitable example on google about how its done. Will be glad is someone will be kind to show me how to implement this. Thanks
Here you go:
You could use the combination of Zend_Auth and Zend_Acl. To extend the other answers I give a short example of how you can manage authentication using zend framework:
First you need to setup a plugin to predispatch all requests and check if the client is allowed to access certain data. This plugin might look like this one:
class Plugin_AccessCheck extends Zend_Controller_Plugin_Abstract {
private $_acl = null;
public function __construct(Zend_Acl $acl) {
$this->_acl = $acl;
}
public function preDispatch(Zend_Controller_Request_Abstract $request) {
//get request information
$module = $request->getModuleName ();
$resource = $request->getControllerName ();
$action = $request->getActionName ();
try {
if(!$this->_acl->isAllowed(Zend_Registry::get('role'),
$module . ':' . $resource, $action)){
$request->setControllerName ('authentication')
->setActionName ('login');
}
}catch(Zend_Acl_Exception $e) {
$request->setControllerName('index')->setActionName ('uups');
}
}
}
So every user type has certain permissions that you define in your acl library. On every request you check if the user is allowed to access a resource. If not you redirect to login page, else the preDispatch passes the user to the resource.
In Zend_Acl you define roles, resources and permission, that allow or deny access, e.g.:
class Model_LibraryAcl extends Zend_Acl {
public function __construct() {
$this->addRole(new Zend_Acl_Role('guests'));
$this->addRole(new Zend_Acl_Role('users'), 'guests');
$this->addRole(new Zend_Acl_Role('admins'), 'users');
$this->add(new Zend_Acl_Resource('default'))
->add(new Zend_Acl_Resource('default:authentication'), 'default')
->add(new Zend_Acl_Resource('default:index'), 'default')
->add(new Zend_Acl_Resource('default:error'), 'default');
$this->allow('guests', 'default:authentication', array('login'));
$this->allow('guests', 'default:error', 'error');
$this->allow('users', 'default:authentication', 'logout');
}
}
Then you have to setup acl and auth in your bootstrap file:
private $_acl = null;
protected function _initAutoload() {
//...your code
if (Zend_Auth::getInstance()->hasIdentity()){
Zend_Registry::set ('role',
Zend_Auth::getInstance()->getStorage()
->read()
->role);
}else{
Zend_Registry::set('role', 'guests');
}
$this->_acl = new Model_LibraryAcl ();
$fc = Zend_Controller_Front::getInstance ();
$fc->registerPlugin ( new Plugin_AccessCheck ( $this->_acl ) );
return $modelLoader;
}
Finally in your authentication controller you have to use a custom auth adapter and setup actions for login and logout:
public function logoutAction() {
Zend_Auth::getInstance ()->clearIdentity ();
$this->_redirect ( 'index/index' );
}
private function getAuthAdapter() {
$authAdapter = new Zend_Auth_Adapter_DbTable (
Zend_Db_Table::getDefaultAdapter ());
$authAdapter->setTableName('users')
->setIdentityColumn('email')
->setCredentialColumn ('password')
->setCredentialTreatment ('SHA1(CONCAT(?,salt))');
return $authAdapter;
}
In your login action you need to pass login data to the auth adapter which performs the authentication.
$authAdapter = $this->getAuthAdapter ();
$authAdapter->setIdentity ( $username )->setCredential ( $password );
$auth = Zend_Auth::getInstance ();
$result = $auth->authenticate ( $authAdapter );
if ($result->isValid ()) {
$identity = $authAdapter->getResultRowObject ();
if ($identity->approved == 'true') {
$authStorage = $auth->getStorage ();
$authStorage->write ( $identity );
$this->_redirect ( 'index/index' );
} else {
$this->_redirect ( 'authentication/login' );
}
And that's all. I recommend you this HOW TO on youtube on zend auth and zend acl.
You may find the following articles on Action Helpers useful, I recommend browsing the sites if any are new to you as they also have articles on implementing Zend_Auth:
Using Action Helpers To Implement Re-Usable Widgets by Matthew Weier O'Phinney
Introducing Action Helpers by Jon Lebensold
Using Action Helpers in Zend Framework by Rob Allen
ZF Reference Guide: Action Helpers Official Documentation
I'm having a difficult time understanding how Zend_Session_Namespace is integrated with Zend_Auth. I have this method I'm using as the action to Authenticate my login page-it is working correctly and redirecting to the /monthly view:
public function authAction(){
$request = $this->getRequest();
$registry = Zend_Registry::getInstance();
$auth = Zend_Auth::getInstance();
$DB = $registry['DB'];
$authAdapter = new Zend_Auth_Adapter_DbTable($DB);
$authAdapter->setTableName('users')
->setIdentityColumn('UserName')
->setCredentialColumn('Password');
// Set the input credential values
$uname = $request->getParam('UserName');
$paswd = $request->getParam('Password');
$authAdapter->setIdentity($uname);
$authAdapter->setCredential($paswd);
// Perform the authentication query, saving the result
$result = $auth->authenticate($authAdapter);
// TRYING TO SET THE NAMESPACE
$this->session = new Zend_Session_Namspace('UserName');
if($result->isValid()){
$data = $authAdapter->getResultRowObject(null,'password');
$auth->getStorage()->write($data);
$this->_redirect('/monthly');
}else{
$this->_redirect('/login');
}
}
But I need to be able to store UserName as a Zend_session and call it from monthly controller. I'm not doing things right because I just get a blank screen when I try and do this:
public function indexAction()
{
$this->view->userName = Zend_Session_Namespace('UserName');
}
With the lines:
$data = $authAdapter->getResultRowObject(null,'password');
$auth->getStorage()->write($data);
You're writing all the user's information, except the password, which is OK. Where ever you need to access the logged in user's details, you can do something like (updated as per your comment):
public function indexAction() {
$auth = Zend_Auth::getInstance();
if($auth->hasIdentity()) {
$userData = $auth->getIdentity();
$this->view->user = $userData;
}
}
in the view file (index.phtml) just: echo $this->user->firstname
That's it. If one day you decide to change the storage for Zend_Auth from session, to, for example, database, this piece of code will still work.
Youre not useing the correct namespace. Zend_Auth use the Zend_Auth namespace. The namespace is the structure, not the key for a value. so your session looks something like this:
Array('Zend_Auth' => array ('UserName' => 'myname')
Well thats not accurate exactly because you havent stored the user name unless youve provided for this directly in your adapter. youll need to do something like:
$auth->getStorage()->UserName = 'myusername';
Then you can access with $authData = new Zend_Session_Namespace('Zend_Auth'); $username = $authData->UserName;.
Take a closer look at how the Zend_Auth_Adapter_Db works.
This is my approach and it s working nice:
1-i start by defining an init function in the bootstrap
protected function _initSession()
{
$UserSession = new Zend_Session_Namespace('UserSession');
$UserSession->setExpirationSeconds(/* you may fix a limit */);
Zend_Registry::set('UserSession', $UserSession);
}
/* in the Login action,after correct username & pwd */
// Create session
$UserSession = Zend_Registry::get('UserSession');
// Get the user from database or just from fields
//you have to make sure that the psswd is encrypted use MD5 for example..
$db = Zend_Db_Table::getDefaultAdapter();
$user = $db->fetchRow("SELECT * FROM user_table WHERE user_email = '".$user_email."'");
//then you assign to $user to $UserSession variable :
$UserSession->user = $user;
//finaly don't forget to unset session variable in the Logout action ...
What is the best practice for user website/REST authentication in ZV MVC? How and where to put the code in the ZF framework? Can you provide me a code example?
I have a website and a REST server written in Zend Framework but no user session jet implemented.
THX!
Authentication is setup in the _initAutoload of the bootstrap file, e.g. like this:
if(Zend_Auth::getInstance()->hasIdentity()) {
Zend_Registry::set('role', Zend_Auth::getInstance()
->getStorage()->read()->role);
}else{
Zend_Registry::set('role', 'guests');
}
In case of a REST authentication you might need to authenticate by just passing the login parameters instead of logging in through a form.
So it might look like this in your AuthenticationController:
private function getAuthAdapter() {
$authAdapter = new Zend_Auth_Adapter_DbTable(
Zend_Db_Table::getDefaultAdapter());
$authAdapter->setTableName('users') // the db table where users are stored
->setIdentityColumn('email')
->setCredentialColumn('password')
->setCredentialTreatment('SHA1(CONCAT(?,salt))');
return $authAdapter;
}
public function logoutAction() {
Zend_Auth::getInstance()->clearIdentity();
$this->_redirect('index/index');
}
public function loginAction(){
if (Zend_Auth::getInstance()->hasIdentity()){
$this->_redirect('index/index');
}
if ($request->isPost()){
$username = $request->getPost('username');
$password = $request->getPost('password');
if ($username != "" && $password != "") {
$authAdapter = $this->getAuthAdapter ();
$authAdapter->setIdentity($username)
->setCredential($password);
$auth = Zend_Auth::getInstance();
$result = $auth->authenticate($authAdapter);
if($result->isValid()){
$identity = $authAdapter->getResultRowObject();
$authStorage = $auth->getStorage();
$authStorage->write($identity);
$this->_redirect ( 'index/index' );
}
}
}
}
If you need more help on zend_auth and zend_acl you might have a look at this how to.