Zend_Auth fails to write to storage - zend-framework

I have yet another weird annoying thing going on with Zend.
Basically, I have the following code after creating a user domain:
$this->auth = Zend_Auth::getInstance();
$this->view->user = $this->user = $this->auth->getIdentity();
$this->user->idSite = $idSite;
$this->user->urlSite = $urlSite;
$this->auth->getStorage()->write($this->user);
What FURIOUSLY annoys me is that the auth->getIdentity() just moments after that:
[idSite] => 0
[urlSite] =>
So from here it gets worse: If I REFRESH or if any of the other parameters of the form fail and send me to the SAME FORM, but WITHOUT TOUCHING THE ABOVE SCRIPT, the auth-getIdentity() correctly returns:
[idSite] => 2431
[urlSite] => exampledomain
Which means that the code is correct and working, BUT if the form is filled out correctly and everything adds up nicely, I redirect to the next step: $this->_redirect('nextstep'), and neither idSite or urlSite remain empty forever.
Why is this? Why?

I've had the same issue and I think it is better to go via the route of using the session namespace functionality:
$oSession = new Zend_Session_Namespace('myStorage');
$oSession->foo = "bar";
$oSession->baz = 123;
And you can recover the data by:
$oSession = new Zend_Session_Namespace('myStorage');
$this->view->foo = $oSession->foo;
There are some more clues here: Zend_Auth the main message of which is that the storage of Zend_Auth data is actually just a namespace.
The default access to this would be similar to :
$oSession = new Zend_Session_Namespace('Zend_Auth');

I also had trouble with Zend_Auth not writing to storage. However, after seeing the answer by Ian Lewis and your response I realised it was probably writing ok, but not reading. I had previously changed the 'name' setting in my session to my own namespace. Once I removed this and started using the default again my Zend_Auth worked fine.

Related

Is it possible to use Log4perl to create per user log files?

I have a mojolicious web app that uses Log4perl for logging. It is a multi user application and sometimes it is difficult to follow the various threads in log file when more than one user is accessing the application. What I would like to do is have each user (the population is under 25 users) activity be logged to a separate file. E.g. ./log/userX.log ./log/userY.log, etc.
I thought about using something like this in the config file:
log4perl.appender.MAIN.filename=sub { return get_user_filename(); }
but the logger is defined in the Mojolicious startup subroutine and the user isn't known until request time.
Another idea that seems more promising, is to write a bridge route that creates an appender for the user and then assigns that to the logger. I could then cache the appender for later reuse (or destroy and recreate).
I'll be playing with the second option, but if anyone has tried to do this before and wants to share their wisdom, I would appreciate it.
-- Update --
So in my bridge route I'm doing the following:
my $user = $self->req->headers->header('authuser'); # from apache
my $appender = Log::Log4perl::Appender->new(
'Log::Log4perl::Appender::File',
name => $user . "_file_appender",
filename => "/tmp/$user.log",
mode => "append",
);
$appender->layout($layout); # previously defined
$appender->level($loglevel); # again previously defined and omitted for brevity
Log::Log4perl::get_logger($user)->add_appender($appender);
$self->app->log(Log::Log4perl::get_logger($user));
I'm getting the following error, however:
Can't locate object method File:() in Log::Log4perl::Appender at /usr/local/share/perl/5.14.2/Log/Log4perl/Appender.pm line 282, <DATA> line 747.
/tmp/user.log is being created, though (zero length) Latest CPAN instal of Log::Log4perl. Any ideas?
I liked Richard's comment and already wrote this up before you said you didn't want to go the DB route. So I include it for others. Aside: I think DB logging is pretty heavy handed sometimes and I personally would typically choose files too.
Somewhere in the top of the request/dispatch cycle (Catalyst example)-
Log::Log4perl::MDC->put( "user", $ctx->user_exists ? $ctx->user->id : 0 );
Then in the Log4perl config-
log4perl.appender.toDBI = Log::Log4perl::Appender::DBI
log4perl.appender.toDBI.Threshold = INFO
log4perl.appender.toDBI.layout = Log::Log4perl::Layout::NoopLayout
log4perl.appender.toDBI.datasource = sub { "DBI:mysql:" . db_name_function() }
log4perl.appender.toDBI.attrs.f_encoding = utf8
log4perl.appender.toDBI.username = db_username
log4perl.appender.toDBI.password = s3cr37
log4perl.appender.toDBI.sql = INSERT INTO toDBI \
( user, file, line, message ) \
VALUES ( ?, ?, ?, ? )
log4perl.appender.toDBI.usePreparedStmt = 1
log4perl.appender.toDBI.params.1 = %X{user}
log4perl.appender.toDBI.params.2 = %F
log4perl.appender.toDBI.params.3 = %L
log4perl.appender.toDBI.params.4 = %m
As far as files go, I think it might be possible but won't be much fun, seems likely to introduce bugs, and is probably overdone. It is trivial to add something like user:{userid} to your logging and then grep/ack the logfile with that to get exactly the user's request/log thread.

Zend_Session_SaveHandler_Interface and a session_id mysterie

I'm trying to setup my own Zend_Session_SaveHandler based on this code
http://blog.digitalstruct.com/2010/10/24/zend-framework-cache-backend-libmemcached-session-cache/
This works great, except that my session_id behave mysteriously.
I'm using the Zend_Session_SaveHandler_Cache class as you can find it in the blog above (except that I parked it in my own library, so it's name now starts with My_).
In my bootstrap I have:
protected function _initSession()
{
$session = $this->getPluginResource('session');
$session->init();
Zend_Session::getSaveHandler()->setCache( $this->_manager->getCache( 'memcached' ) );
}
To get my session going based on this code in my .ini file
resources.cachemanager.memcached.frontend.name = Core
resources.cachemanager.memcached.frontend.options.automatic_serialization = On
resources.cachemanager.memcached.backend.name = Libmemcached
resources.cachemanager.memcached.backend.options.servers.one.host = localhost
resources.cachemanager.memcached.backend.options.servers.one.port = 11213
So far so good. Until somebody tries to login and Zend_Session::rememberMe() is called. In the comments of Zend_Session one can read
normally "rememberMe()" represents a security context change, so
should use new session id
This of course is very true, and a new session id is generated. The users Zend_Auth data, after a successful log in, is written into this new session. I can see this because I added some logging functionality to the original class from the blog.
And here is where things go wrong. This new id isn't passed on the Zend_Session apparently, because Zend_Session keeps on reading the old id's session data. In other words, the one without the Zend_Auth instance. Hence, the user can no longer log in.
So the question is, how to make my saveHandler work with the new id after the regeneration?
Cheers for any help.
Ok, I'm blushing here....
I was looking at the wrong place to find this error. My session saveHandler was working just fine (so I can recommend Mike Willbanks his work if you want libmemcached session management).
What did go wrong then? Well, besides switching from file to libmemcached, I also switched from setting up my session in bootstrap to setting it up in my application.ini. So, instead of putting lines like
session.cookie_domain = mydomain.com
in my application.ini (which were then used in bootstrap as options to setup my session), I now, properly, wrote
resources.session.cookie_domain = mydomain.com
And this is were things went wrong, because.... I only changed those lines for production, I forgot to change them further down the ini file. In other words, my development env. got the cookie_domain of my production env., which is wrong as I use an other domain name during devolepment. So, on every page load, my cookie was invalidaded and a new session started. Mysterie solved...

How to handle Zend Framework End User INI/Config settings

I have searched and searched for this but I think my terminology isn't correct as it keeps giving me the application settings for the zend site rather than an application settings for the End User.
I'd like to have a config.ini type file that the end user can edit values in. I'd like it to be ONLY the settings I wish them to see and to be able to create the value names as I think would make sense to them. So it would be something like
[General]
SiteName=MySite
ShowResources=TRUE
[Database]
Server=myServer
databasepath=mydbpath
...
So my two questions.
1. What is this type of file called because when I search application settinsg, I get the ZF application settings not one for an end user (presumably)
What is the best way to handle this type of file?
Thanks
In your bootstrap add:
protected function _initConfig()
{
$config = new Zend_Config_Ini(APPLICATION_PATH.'/configs/config.ini');
Zend_Registry::set('config', $config);
return $config;
}
replace config.ini with whatever you want the filename to be.
You can then access this config object anywhere in your application either as an application resource or through the registry (Zend_Registry::get('config')). So to get the SiteName from your example:
$config = Zend_Registry::get('config');
echo $config->General->SiteName;
For things like database settings, you'll want to access these in the bootstrap so you can use them to setup other resources. I would recommend you don't try and include database settings in your application.ini as well, instead manually setup the DB resource by adding another bootstrap method:
protected function _initDb()
{
$this->bootstrap('config');
$config = $this->getResource('config');
$db = Zend_Db::factory('Pdo_Mysql', array(
'host' => $config->Database->Server,
'username' => $config->Database->Username,
'password' => $config->Database->Password,
'dbname' => $config->Database->Dbname
));
return $db;
}
To explain this some more, $this->bootstrap('config'); ensures the config resource is loaded first. $this->getResource('config'); returns the config resource (the one created by the _initConfig() method). It then uses the data from this object to create the DB connection.
It's an INI file, which you can read and write via Zend_Config.
ZF has no concept of "user settings" -- users are defined by you, not by the framework.
Apps usually store user configs in a database, but that's totally up to you. You could store a directory of INI files instead. Either way, you have to do the implementation yourself.
Edit: Given that you have a ZF app that you're distributing to the customer, and they're only ever going to connect to one database with it, that changes things significantly. (I thought you originally meant that you'd have one instance of the app simultaneously connecting to multiple databases.)
In your case, I would use the standard ZF application/configs/application.ini file for your application's "internal" settings. Then, I'd have a separate local.ini (or whatever) in that same application/configs directory, which contains only those settings that you want the customer editing. Distribute a skeleton local.ini file with the app, that has instructions right in it, something like this:
; Remove the comment from this line.
;configured = 1
; You need to put your database credentials in here.
db_host = "PUT YOUR DATABASE SERVER NAME HERE"
db_user = "PUT YOUR DATABASE USERNAME HERE"
db_pass = "PUT YOUR DATABASE PASSWORD HERE"
Then just load the local.ini file via Zend_Config. I'd also add a check to your index controller's init method that checks to see if you're properly configured:
$localConfig = Zend_Registry::get('local_config'); // or wherever you put it
if (!$localConfig->configured) {
$this->_helper->redirector('config', 'error');
}
And then make a error/config view that says:
You didn't read the instructions. Go do that now.
Note there's nothing stopping the customer from editing anything they want, but this makes a logical separation and makes it harder to accidentally screw something up.

How to add some extra parameter in the airbrake parameters for JS errors

When we are sending the airbrake error to the airbrake server, by default it includes the controller name and action name.
But the question is that I want to add some extra parameters like username, email of the current user. If anyone has any idea please suggest how to do that?
In my layout application.html:
- if ['development'].include?(Rails.env)
= airbrake_javascript_notifier
= render :partial => 'layouts/airbrake_notifier'
and in the partial I have written:
Airbrake.errorDefaults['name'] = "#{current_user.name}";<br/>
Airbrake.errorDefaults['email'] = "#{current_user.email}";<br/>
Airbrake.errorDefaults['phone'] = "#{current_user.phone}";<br/>
Airbrake.errorDefaults['title'] = "#{current_user.title;<br/>
Not a great solution, but the Airbrake Knowledge Base recommends essentially patching the airbrake gem source of the lib/airbrake/notice.rb file.
def initialize(args)
...
self.parameters = args[:parameters] ||
action_dispatch_params ||
rack_env(:params) ||
{'username' => current_user.name}
It would certainly be better to have this be configurable without patching source.
What I've done instead is simply add a few pieces of data to the session (current_user.name mainly), since session data is sent with the request. I wouldn't do this for more than a few little pieces of data.
We've just added getting current users into the Airbrake Gem.
https://github.com/airbrake/airbrake/wiki/Sending-current-user-information
You'll soon be able to sort by current user in an upcoming redesign of the UI.

view helper in zend framework 2

the thing is these this lines:
$loginUrl = $this->view->url(array('controller'=>'auth', 'action'=>'index'));
$registerUrl = $this->view->url(array('controller'=>'register', 'action'=>'index'));
based on rob allens' Zend_Auth login/logout tutorial (win7/apache),
are placed in a view helper, and this one:
echo $this->url(array('controller'=>'index','action'=>'add'));
is placed in the index view script.
The generated links Do work fine in LOCAL, but in REMOTE only the 3rd line works.
ANY IDEAS? Where should i look for this? wich way to follow?
I was tempt to think in the remote server conf but the 3rd line works fine, so..
thanks!
Try this helper instead of view Zend_Controller_Action_Helper_Url:
//simple($action, $controller = null, $module = null, array $params = null)
//so your lines will look like:
$loginUrl = $this->_helper->url->simple('index','auth');
$registerUrl = $this->_helper->url->simple('index','register');
P.S. your lines work properly on Win7 and Ubuntu servers check registry of the lines
I found out that was the server. (.htacces and mod_rewrite) was not included in the package.
I think the third line was working because it was in the index controller, but when calling the others, then happened the object not found.
To work out this, i found an example using zend debug (was in german) so i inferred it (and then wrote to the hosting service), but still not quite sure how to check (phpinfo?) if a host have this features available or not in your package.