How does Zend_Auth storage work? - zend-framework

This is very simple. I write
$auth->getStorage()->write($user);
And then I want, in a separate process to load this $user, but I can't because
$user = $auth->getIdentity();
is empty. Didn't I just... SET it? Why does it not work? Halp?
[EDIT 2011-04-13]
This has been asked almost two years ago. Fact is, though, that I repeated the question in July 2010 and got a fantastic answer that I back then simply did not understand.
Link: Zend_Auth fails to write to storage
I have since built a very nice litte class that I use (sometimes with extra tweaking) in all my projects using the same storage engine as Zend_Auth but circumventing all the bad.
<?php
class Qapacity_Helpers_Storage {
public function save($name = 'default', $data) {
$session = new Zend_Session_Namespace($name);
$session->data = $data;
return true;
}
public function load($name = 'default', $part = null) {
$session = new Zend_Session_Namespace($name);
if (!isset($session->data))
return null;
$data = $session->data;
if ($part && isset($data[$part]))
return $data[$part];
return $data;
}
public function clear($name = 'default') {
$session = new Zend_Session_Namespace($name);
if (isset($session->data))
unset($session->data);
return true;
}
}
?>

It's supposed to work.
Here's the implementation of the Auth getIdentity function.
/**
* Returns the identity from storage or null if no identity is available
*
* #return mixed|null
*/
public function getIdentity()
{
$storage = $this->getStorage();
if ($storage->isEmpty()) {
return null;
}
return $storage->read();
}
Here's the implementation of the PHP Session Storage write and read functions:
/**
* Defined by Zend_Auth_Storage_Interface
*
* #return mixed
*/
public function read()
{
return $this->_session->{$this->_member};
}
/**
* Defined by Zend_Auth_Storage_Interface
*
* #param mixed $contents
* #return void
*/
public function write($contents)
{
$this->_session->{$this->_member} = $contents;
}
Are you sure you are loading the same instance of the Zend_Auth class?
Are you using
$auth = Zend_Auth::getInstance();
Maybe you are calling the write method after the getIdentity method?
Anyway, as I said before, what you are doing should work.

So on a page reload you can fetch the session, while not if redirecting? Are you redirecting on a different Domain-Name? Then it might be an issues with the Cookies and you'd need to manually set session.cookie_domain ini variable.
Check if the cookie named PHPSESSID has been properly been set and if it's being sent to the server on every page request? Is it constant or does it change on every request?
Also, you might want to check if the session data is being properly saved to the disk. The sessions can be found in the directory defined by the ini variable session.save_path. Is the file corresponding to your PHPSESSID there and does it contain a meaningful entry? In my case it contains
root#ip-10-226-50-144:~# less /var/lib/php5/sess_081fee38856c59a563cc320899f6021f
foo_auth|a:1:{s:7:"storage";a:1:{s:9:"user_id";s:2:"77";}}

Add:
register_shutdown_function('session_write_close');
to index.php before:
$application->bootstrap()->run();

Related

I am using \Magento\Customer\Model\SessionFactory $customerSession in my block but it is not working without cacheable false

If we need to get current customer session in a block, cacheable must be set to false in the layout xml. I am using \Magento\Customer\Model\SessionFactory $customerSession in my block but it is not working without cacheable false. Can someone tell me a recommended way to solve this problem.
Session is cached in blocks by default. However you can use a cacheable = false to use a Session in block but is not recommended.
I recommend to use a Registry to store/get information in blocks. If you need specific info from Session you can set this info in a Registry from Controller of your Block.
Here is some info: https://meetanshi.com/blog/use-registry-in-magento-2/
Good Luck !
Can you please check this? It Might be help you.
namespace Vendor\Module\Block;
class Form extends \Magento\Framework\View\Element\Template
{
protected $customerSession;
/**
* Construct
*
* #param \Magento\Framework\View\Element\Template\Context $context
* #param \Magento\Customer\Model\Session $customerSession
* #param array $data
*/
public function __construct(
\Magento\Framework\View\Element\Template\Context $context,
\Magento\Customer\Model\Session $customerSession,
array $data = []
) {
parent::__construct($context, $data);
$this->customerSession = $customerSession;
}
public function _prepareLayout()
{
var_dump($this->customerSession->getCustomer()->getId());
exit();
return parent::_prepareLayout();
}
}

Update object in hook

I'm using a hook after the user unhide a record. In this hook i want to update a object.
class ProcessCmdmap {
function processDatamap_postProcessFieldArray($status, $table, $id, &$fieldArray, &$reference) {
if ($table == 'tx_oaevents_domain_model_events' && $status == 'update' && $fieldArray['hidden'] == 0) {
// Get objectmanager
$objectManager = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Extbase\\Object\\ObjectManager');
// Get repo
$repository = $objectManager->get('Mab\\Oaevents\\Domain\\Repository\\EventsRepository');
// Get config manager
$configurationManager = $objectManager->get('TYPO3\\CMS\\Extbase\\Configuration\\ConfigurationManagerInterface');
// Get settings and storage pid
$settings = $configurationManager->getConfiguration(\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface::CONFIGURATION_TYPE_FULL_TYPOSCRIPT);
$storagePid = $settings['plugin.']['tx_oaevents.']['persistence.']['storagePid'];
// Build default query settings
$querySettings = $objectManager->get('TYPO3\\CMS\\Extbase\\Persistence\\Generic\\Typo3QuerySettings');
$querySettings->setStoragePageIds(array($storagePid));
$repository->setDefaultQuerySettings($querySettings);
$object = $repository->findByUid($id);
// modify object
// Update / Persist object
}
}
}
But i can't retrieve a object with "findByUid()" or "findAll()". Do i use the objectmanager in wrong scope? Or how can retrieve and update into hooks my object?
Can someone give me a hint?
Update:
Now i give up :( and not use the objectmanager and repository, i use the functions from $GLOBALS['TYPO3_DB']
I definitely recommend to follow your solution by using $GLOBALS['TYPO3_DB'] at this point. Extbase just brings in a lot of overhead here.
Without Extbase you also need less code here.
Anyway:
Most likely the generated queries by Extbase are broken. Log your queries and check the generated one. Try it out and modify it, until it works. Check the difference.
You can try without using "setStoragePageIds".
If you are looking record by uid it is useless.
My working code for processDatamap:
/** #var $objectManager \TYPO3\CMS\Extbase\Object\ObjectManager */
$objectManager = GeneralUtility::makeInstance('TYPO3\\CMS\\Extbase\\Object\\ObjectManager');
/** #var $orderRepository \Vendor\ExtensionName\Domain\Repository\OrderRepository */
$orderRepository = $objectManager->get('Vendor\\ExtensionName\\Domain\\Repository\\OrderRepository');
$order = $orderRepository->findByUid($id);
It is true that the use of $GLOBALS['TYPO3_DB'] is easier.

Extbase update deleted=1 object in Controller fails (Object with identity x not found)

I want do give the function to 'restore' deleted Object in my FE-Ext. It seems, that it does not find any deleted records an so i cannot update them set deleted = 0.
What you be you sugestion to handle that from the controller?:
$query->getQuerySettings()->setIgnoreEnableFields(TRUE);
$query->getQuerySettings()->setIncludeDeleted(TRUE);
Thank you.
Im not quite sure what you mean by "from the controller". Normally you implement this in your repository and just call the method from the controller.
In your repo:
public function findRecordEvenIfItIsDeleted($uid) {
$query = $this->createQuery();
$settings = $query->getQuerySettings();
settings->setIgnoreEnableFields(TRUE);
settings->setIncludeDeleted(TRUE);
$query->matching($query->equals('uid', $uid));
return $query->execute();
}
In your controller:
$myObject = $this->myRepsository->findRecordEvenIfItIsDeleted($uid);
Done. (Of course your storage pid must be set (or disable respectStoragePage as well)
You're adding does not throw any error because you are setting the querySettings to include deleted records. But maybe, this setting has to be enabled even when you are updating, as the repository should find the object you are updating.
I haven't tested it but give this a try.
In your repository(just a pseudo code)
public function update($modifiedObject) {
settings->setIncludeDeleted(TRUE);
parent::update($modifiedObject);
}
I know this question was asked long time ago but today i had a similar problem with a hidden object. My solution was this one:
add this to your Model (in your case exchange "hidden" by "deleted"):
/**
* #var boolean
*/
protected $hidden;
/**
* #return boolean $hidden
*/
public function getHidden() {
return $this->hidden;
}
/**
* #return boolean $hidden
*/
public function isHidden() {
return $this->getHidden();
}
/**
* #param boolean $hidden
* #return void
*/
public function setHidden($hidden) {
$this->hidden = $hidden;
}
in your repository add this function to find the deleted/hidden object:
public function findHiddenByUid($uid) {
$query = $this->createQuery();
$query->getQuerySettings()->setIgnoreEnableFields(TRUE);
$query->getQuerySettings()->setEnableFieldsToBeIgnored(array('disabled','hidden'));
return $query
->matching($query->equals('uid', $uid))
->execute()
->getFirst();
}
now in your Controller you can read the object, set the "hidden" option and update it:
$yourobject = $this->yourobjectRepository->findHiddenByUid($uid);
$yourobject->setHidden(1);
$this->yourobjectRepository->update($yourobject);
Maybe not interesting for your task but for others:
In my case i additionally had the problem posting a hidden object in a form to an action. So note that if you want to post an object by a form, it is better (or probably necessary) to first set the objects deleted/hidden option to 0.

Can (and should?) Zend_Auth return class as the Identity?

I have a class R00_Model_User, which, curiously enough, represents user as he is. Can $result->getIdentity() return me an object of this class? (Or maybe it's stupid?)
(There is a factory method in R00_Model_User which prevents from duplicating objects. I'd like Zend_Auth to use it instead of creating a new object, if it can)
Two options:
write your own authentication adapter subclassing the out-of-the-box-adapter that matches your scenario best
class R00_Auth_Adapter extends Zend_Auth_Adapter_*
{
/**
* authenticate() - defined by Zend_Auth_Adapter_Interface. This method is called to
* attempt an authentication. Previous to this call, this adapter would have already
* been configured with all necessary information to successfully connect to a database
* table and attempt to find a record matching the provided identity.
*
* #throws Zend_Auth_Adapter_Exception if answering the authentication query is impossible
* #return Zend_Auth_Result
*/
public function authenticate()
{
$result = parent::authenticate();
if ($result->isValid() {
return new Zend_Auth_Result(
$result->getCode(),
R00_Model_User::load($result->getIdentity()),
$result->getMessages()
);
} else {
return $result;
}
}
}
This will allow you to code
$adapter = new R00_Auth_Adapter();
//... adapter initialisation (username, password, etc.)
$result = Zend_Auth::getInstance()->authenticate($adapter);
and on successfull authentication your user-object is automatically stored in the authentication storage (session by default).
or use your login-action to update the stored user identity
$adapter = new Zend_Auth_Adapter_*();
$result = $adapter->authenticate();
if ($result->isValid()) {
$user = R00_Model_User::load($result->getIdentity());
Zend_Auth::getInstance()->getStorage()->write($user);
}
In one of my applications, I have getIdentity() return a user object, and it works pretty well for me. To use your factory method, do like this:
$auth = Zend_Auth::getInstance();
$user = R00_Model_User::getInstance(...);
$auth->getStorage()->write($user);
Then when you call getIdentity(), you will have your user object.

Zend Framework Authentication and Redirection

What is the best method in Zend Framework to provide restricted areas and redirect users to a login page? What I want to do is set a flag on my controllers for restricted pages:
class AdminController extends Zend_Controller_Action
{
protected $_isRestricted = true;
....
and have a plugin check to see if the controller is restricted and if the user has authenticated, otherwise redirect them to the login page. If I do this directly in the controller's preDispatch I can use $this->_redirect(), but looking at Action Helpers they won't have access to that. It's also a lot of duplicate code to copy/paste the authentication check code in every controller that needs it.
Do I need an Action Controller linked to preDispatch, or a Front Controller plugin? How would I do the redirect and still preserve things like the base URL?
Use Zend_Acl (best combined with Zend_Auth)
See also Practical Zend_ACL + Zend_Auth implementation and best practices
For one project, I've extended Zend_Controller_Action, and in that class's preDispatch put a check for logged-in-ness. I can override it on a per-action basis with an init() that checks the actionname and turns off the requirement (or preDispatch() that calls it's parent for the actual checks).
In a project I was working on, I had trouble with various users experiencing timeouts from their browsers. This meant the Zend_Auth no longer existed in the registry and users lost access to required pages/functions.
In order to stop this from occuring, I setup a Plugin (as you suggest) and have this plugin perform checks in the preDispatch(). An example is below:
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
public function run()
{
$front = Zend_Controller_Front::getInstance();
$front->registerPlugin(new App_Controller_Plugin_Timeout());
parent::run();
}
}
with the timeout class implementing any Zend_Auth or Zend_Acl requirements, using a check via the function below.
class App_Controller_Plugin_Timeout extends Zend_Controller_Plugin_Abstract
{
/**
* Validate that the user session has not timed out.
* #param Zend_Controller_Request_Abstract $request
* #return void
* #todo Validate the user has access to the requested page using Zend_Acl
*/
public function preDispatch(Zend_Controller_Request_Abstract $request)
{
$frontController = Zend_Controller_Front::getInstance();
$controllerName = $frontController->getRequest()->getControllerName();
$actionName = $frontController->getRequest()->getActionName();
$authInstance = Zend_Auth::getInstance();
/** If the controller is not the Auth or Error controller, then check for
* a valid authorized user and redirect to the login page if none found */
if (($controllerName !== 'auth') && ($controllerName !== 'index') && ($controllerName !== 'error')) {
if (!$authInstance->hasIdentity()) {
$this->_response->setRedirect('/index/timeout')->sendResponse();
exit;
}
} else if (($controllerName == 'index') || (($controllerName == 'auth') && ($actionName !== 'logout'))) {
/** If running the Auth or Index (default) controller (and not the logout
* action), check if user already signed in and redirect to the welcome page */
if ($authInstance->hasIdentity()) {
$this->_response->setRedirect('/general/welcome')->sendResponse();
exit;
}
}
}
}
....
/**
* Test that the input user belongs to a role based on the user input and
* the values loaded into the Acl registry object setup when the site first
* loads
*
* #param mixed|Zend_Auth $userData
* #param string $userRole
* #return boolean
* #throws Zend_Exception When invalid input is provided
*/
public function isUserMemberOfRole($userData, $userRole)
{
if (empty($userData)) {
$auth = Zend_Auth::getInstance();
if($auth->hasIdentity()) {
$userData = $auth->getIdentity();
} else {
return FALSE;
}
}
if (!is_string($userRole)){
throw new Zend_Exception('Invalid input provided to ' . __METHOD__);
}
// Setup the required variables and access the registry for the Acl values
$rolesTable = new App_Model_Internal_UsersToRoles();
$registry = Zend_Registry::getInstance();
$acl = $registry->get('acl');
$roles = $rolesTable->getUserRoles($userData); // returns an array of values
foreach ($roles as $value) {
if ($value['Name'] == $userRole) {
return $acl->isAllowed($value['Name'], null, $userRole);
}
}
}
I had the user access implemented in a database table and then initialized as an "_init" function at Bootstrap->run() as follows:
protected function _initAclObjectForUserRoles()
{
$userTable = new App_Model_Internal_Roles();
$acl = new Zend_Acl();
$userRoles = $userTable->fetchAll();
$roles = $userRoles->toArray();
// Cycle through each Role and set the allow status for each
foreach($roles as $value) {
$department = $value['Name'];
$acl->addRole(new Zend_Acl_Role($department));
$acl->allow($department, null, $department);
}
// Add the new Acl to the registry
$registry = Zend_Registry::getInstance();
$registry->set('acl', $acl);
}
So, using this method you could put access restrictions via the roles loaded via from a database into an Zend_Acl object, or you could load the controller class attribute via the Timeout plugin and check it's value. Although, I've found it's easier to maintain access policies in a database than spread them throughout your code base... :-)