Ask your Symfony questions! Pay money and get answers fast! (more info)

Task requires login credentials Symfony

Hi,

I have an action defined in one of my applications that need to be executed by a cron job. I have written a task file, but I've run into a problem. When I run the task, it outputs the login screen to my terminal.

The action is protected by credentials, which is what I want for the web-facing side. The action should only be run by people with administrator privileges. However, I need a cron job to run the same action, too.

How can I provide some authentication to the task so that it runs properly?


-Steve

class sendRemindersTask extends sfBaseTask
{
protected function configure()
{
$this->addOptions(array(
new sfCommandOption('application', 'backend', sfCommandOption::PARAMETER_REQUIRED, 'The application name'),
new sfCommandOption('env', 'prod', sfCommandOption::PARAMETER_REQUIRED, 'The environment', 'dev'),
new sfCommandOption('connection', null, sfCommandOption::PARAMETER_REQUIRED, 'The connection name', 'doctrine'),
// add your own options here
));

$this->namespace = 'cccsched';
$this->name = 'sendReminders';
$this->briefDescription = 'Sends reminders for clinics that are scheduled in the next 30 hours.';
}

protected function execute($arguments = array(), $options = array())
{
define('SF_ROOT_DIR', realpath(dirname(__FILE__).'/../..'));
define('SF_APP', 'backend');
define('SF_ENVIRONMENT', 'dev');
define('SF_DEBUG', true);

// Use the ProjectConfiguration class for configuration data
require_once(SF_ROOT_DIR.DIRECTORY_SEPARATOR
. 'config' . DIRECTORY_SEPARATOR
. 'ProjectConfiguration.class.php');

$context = sfContext::createInstance($this->configuration);
$controller = $context->getController();
$controller->forward('schedule', 'sendClinicReminders');

}
}

Answers (4)

2011-10-12

Luis Cordova answers:

Steve yes please post the task I will help you

i think you can wrap the secure command within a regular command

and within the second the regular command just authenticate via token

getting the container to authenticate the user is easy if you extend the container aware class and do this:


protected function execute(InputInterface $input, OutputInterface $output)
{
$name = $input->getArgument('name');
$translator = $this->getContainer()->get('translator');
if ($name) {
$output->writeln($translator->trans('Hello %name%!', array('%name%' => $name)));
} else {
$output->writeln($translator->trans('Hello!'));
}
}


And then use impersonation:

[[LINK href="http://symfony.com/doc/current/book/security.html#impersonating-a-user"]]http://symfony.com/doc/current/book/security.html#impersonating-a-user[[/LINK]]

that should do.


Steven Cogorno comments:

Hi Luis,

Thanks for looking at this. Just to be clear, the task will be run from a cron script, so there won't be any user associated with the task.

I added the task to my original post.

Thanks!


Luis Cordova comments:

and also use this:

https://github.com/FriendsOfSymfony/FOSUserBundle/blob/master/Controller/RegistrationController.php#L120

"create a UsernamePasswordToken and set it on the security context"

there is also a code snippet around for this

$token = new UsernamePasswordToken($player, null, 'player_area', $player->getRoles());
$this->container->get('security.context')->setToken($token);

or similar, you get the idea. I think you can create a default user and then impersonate/setting the security context. It would be good to see the end result so email me at cordoval at gmail

thanks!


Luis Cordova comments:

o wow just realized you are on sf1. I was all along talking about sf2... hmmm


Steven Cogorno comments:

Luis,

yes, I am using Symfony 1.4. I have been thinking about upgrading to 2.0, but it doesn't seem that the existing projects are compatible with Symfony 2.0.

Is there some sort of migration path, short of re-engineering the whole application?

-Steve


Luis Cordova comments:

if you know sf2 it can be done, I have heard some people is actually doing that quite a lot or was doing it, 1.4 is not good in my eyes anymore sorry

2011-10-13

Gabor Fasi answers:

Since the relevant code is used in two places, you should place it in a utility class (this is usually done with a class full of static method), and call that from both the action and the task.

This function should not care about authentication, that should be done in the action before calling it. This also makes it easy to use it in a task.

2011-10-13

Giorgio Cefaro answers:

Why don't you just authenticate the user inside the task?
You have the sfContext instantiated, so the sfUser should be too.
What you need to do is to set the sfUser authenticated and add the credentials needed for the target action:

protected function execute($arguments = array(), $options = array())
{
define('SF_ROOT_DIR', realpath(dirname(__FILE__).'/../..'));
define('SF_APP', 'backend');
define('SF_ENVIRONMENT', 'dev');
define('SF_DEBUG', true);

// Use the ProjectConfiguration class for configuration data
require_once(SF_ROOT_DIR.DIRECTORY_SEPARATOR
. 'config' . DIRECTORY_SEPARATOR
. 'ProjectConfiguration.class.php');

$context = sfContext::createInstance($this->configuration);
$controller = $context->getController();
$user = sfContext::getInstance()->getUser();
$user->setAuthenticated(true);
$needed_credentials = array('foobar', 'foo');
$user->addCredentials($needed_credentials);
$controller->forward('schedule', 'sendClinicReminders');
}


Steven Cogorno comments:

Hmm, that's an interesting thought. Who would the user be? Or does it matter?

If I'm understanding you correctly, the task would have an "anonymous" user with admin credential.

I'll give it a try, thanks!


Steven Cogorno comments:

Giorgio,

I tried your suggestion, but I got a 403 Forbidden error:


>> myUser User is authenticated
>> myUser Add credential(s) "admin"
>> sfWebResponse Send status "HTTP/1.0 403 Forbidden"


The admin credential is the correct credential, so I don't know why it fails to authenticate.

Any ideas?

2011-10-13

José Nahuel Cuesta Luengo answers:

Why don't you wrap the actual logic of the task you want to perform in a class (or a bunch of classes, according to its correct design) and then just use that classes in both of the cases you need them:

On hand, in the web env your action will make use of the class (or classes) after delegating the security concerns to the framework, as usual; and on the other hand, in the cli env, you just make use of the class (or classes) without the need of authentication.

I hope I'm making myself clear here. The main idea is that isolating the common logic of the task in a separate object will make your job just easier.

Regards,
Nahuel.


Steven Cogorno comments:

Nahuel,

I thought about doing that, but the code sends emails using templates that are part of the module.

It seemed worse to duplicate the template and partials than to leave this in the module.

-Steve