doctrine, howto, php, symfony Using sfDoctrineGuardUser’s external authentication

0 Comments

sfDoctrineGuardUser has a neat ability to allow you to use an external (the examples are all for LDAP, but would work for anything) authentication process for authenticating your users.

This is really helpful, assuming that you don’t mind manually adding all users in the LDAP directory to the sf_guard_user table. This is because the way that the plugin determines if you are authenticated in sfGuardValidatorUser::doClean contains this

// user exists?
if ($username &&
    $user = Doctrine::getTable('sfGuardUser')->retrieveByUsername($username))
{
      // password is ok?
      if ($user->getIsActive() && $user->checkPassword($password))
      {
        return array_merge($values, array('user' => $user));
      }
}

The user is retrieved from the database before anything else happens.
Unfortunately, that means that if the username doesn’t exist in the local database, you won’t ever get to the point where it will check against your fancy external authentication code.

This post is about how to resolve that issue.

Firstly, you need to change the form that is used for rendering the sgGuardAuth signin action. Do this by adding the following line to your app.yml under “all”

sf_guard_plugin_signin_form: myAuthSigninForm

as described on the symfony docs

Then you need to create that file (I put it in /apps/frontend/lib) and overwrite configure so that you can set it to use your custom validator

class sfGuardCustomFormSignin extends sfGuardFormSignin
{
    public function configure(){
        parent::configure();
        $this->validatorSchema->setPostValidator(new sfGuardValidatorCustomUser());
    }
}

This will allow you to use the custom validator that you are going to put in a library directory somewhere.

I put it here:
apps/frontend/modules/sfGuardAuth/lib/sfGuardValidatorCustomUser.class.php

This validator should extend sfGuardValidatorUser and you will need to overwrite doClean. In the example below the highlighted section is the code that we added to the doClean method.

class sfGuardValidatorCustomUser Extends sfGuardValidatorUser
{
protected function doClean($values)
  {

    $username = isset($values[$this->getOption('username_field')]) ? $values[$this->getOption('username_field')] : '';
    $password = isset($values[$this->getOption('password_field')]) ? $values[$this->getOption('password_field')] : '';

    //This is the authentication code that you want to replace it with
    if($user = myAuth::authenticate($username, $password)) {
        return array_merge($values, array('user' => $user));
    }

    if ($this->getOption('throw_global_error'))
    {
      throw new sfValidatorError($this, 'invalid');
    }

    throw new sfValidatorErrorSchema($this,
      array($this->getOption('username_field') =>
            new sfValidatorError($this, 'invalid')));
  }
}

Then all you need to do is to build out your custom authentication mechanism.

I have mine creating or updating the user if it doesn’t exist, because that makes it easy to keep sfDoctrineGuardUser happy.

The actual authentication mechanism exists in another class altogether.

class svExternalAuth
{
    public static function authenticate($username, $password){
        if( svCustomAuth::authenticate($username, $password) ) {
            return self::cleanUser($username, $password);
        }
    }

    //create and or update
    protected static function cleanUser($username, $password){
        $user = self::getUser($username);
        $user->setPassword($password);
        $user->setIsActive(true);
        //set other user aspects
        $user->save();
        return $user;
    }

    protected static function getUser($username){
        if($user = Doctrine::getTable('sfGuardUser')->
                   retrieveByUsername($username)) {
            //user exists, return it
            return $user;
        } else {
            //none found, create it.
           $user = new sfGuardUser();
           $user->setUsername($username);
           return $user;
        }
    }
}

php, symfony Embedding login form in your homepage with symfony

0 Comments

  • add this to your action (taken from sfGuardAuthActions)
        $class = sfConfig::get('app_sf_guard_plugin_signin_form', 'sfGuardFormSignin');
        $this->form = new $class();
    
  • add this to your template (from the sfGuardAuth template)
          <form action="<?php echo url_for('@sf_guard_signin') ?>"
                      method="post">
          <table>
            <?php echo $form ?>
          </table>
    
          <input type="submit" value="<?php echo __('sign in') ?>" />
          <a href="<?php echo url_for('@sf_guard_password') ?>">
          <?php echo __('Forgot your password?') ?></a>
        </form>
    
  • create your favicon.ico to address this bug.

jQuery, php, symfony Correct jq_button_to_remote complete handler response var

0 Comments

Symfony docs claim that in the ‘complete’ callback you have access to the response as request.responseText.

From svn

* To access the server response, use ‘request.responseText’, to
* find out the HTTP status, use ‘request.status’.

This has not been my experience. When inspecting the variables in scope, I was only able to find, and subsequently access

XMLHttpRequest.responseText;
XMLHttpRequest.status;

OS X Manually set a breakpoint in safari (and in chrome on OSX)

0 Comments

To manually set a breakpoint add the line ‘debugger;’

So if I have some js in an onclick, and I’m trying to look at the scope there, just add ‘debugger;’ to the onclick.


click me

doctrine, php, symfony Adding custom fields to the admin generator

0 Comments

The documentation leaves out one thing in regard to adding your own “virtual field” to the admin generator. In addition to everything in the docs, You have to override the toArray method.

The documentation says:

Custom Fields
As a matter of fact, the fields configured in generator.yml don’t even need to correspond to actual columns defined in the schema. If the related class offers a custom getter, it can be used as a field for the list view; if there is a getter and/or a setter, it can also be used in the edit view. For instance, you can extend the BlogArticle model with a getNbComments() method similar to the one in Listing 14-10.

And you need to do those things, but there’s one missing step. The edit page gets the default for the widgets from the toArray() method, which just iterated over the columns. In order to populate your widget for your “virtual column”, you need to override toArray().

The steps in full are:

  • Add the new field to the generator.yml
  • Add a getter and a setter to the model
  • Add a widget and validator
  • Overwrite the toArray() method in the object

Doctrine example (in the model)

public function toArray($deep = true, $prefixKey = false)
    {
        $arr = parent::toArray($deep, $prefixKey);
        $arr['virtual_column'] = $this->getVirtualColumn();
        return $arr;
    }

    public function getVirtualColumn() {
        return 'hellow world';
    }

    public function setVirtualColumn($string) {
        //do something
    }

doctrine, howto, php, symfony Customizing Admin Generator Filters in Symfony Using Doctrine

0 Comments

How to extend the existing filters in the symfony doctrine admin generator

Here is our (simplified) schema:

Group: [id, group_name]
User: [username, group_id]
Phonenumber: [user_id, num]

Groups contain many users, and users contain many phone numbers.

The prebuilt admin generator will allow you to filter on any of the existing fields in the table for the object that you are viewing, so if we were to visit /phonenumber/index then we would see all the phonenumbers and be able to filter by user.

However, if we want to filter, for instance, by group, then we need to follow a few steps:

  • Add group_id to phonenumber/config/generator.yml filter list
    config:
        filter:
            display: [user_id, group_id]
    
  • Add widget/validator to PhonenumberFormFilter
    We need to alter the filter form to add the select box for groups, and add the validator so that we can sanitize and get the value back.

    public function configure()
    {
        $this->setWidget('group_id',
                         new sfWidgetFormDoctrineChoice(array('model' => 'Group',
                                                              'add_empty' => true)));
    
        $this->setValidator('group_id',
                            new sfValidatorDoctrineChoice(array('required' => false,
                                                                'model' => 'Group',
                                                                'column' => 'id')));
    }
    
  • Add field to PhonenumberFormFilter
    Just so that the filter knows what to filter on we add this method.

    public function getFields()
      {
          return array_merge((array('group_id' => 'ForeignKey''),
                             parent::getFields());
      }
    
  • Alter query to PhonenumberFormFilter
    This is where we alter the actual query. We are adding a join against the user table, and then a where with a group_id.

    public function addGroupIdColumnQuery(Doctrine_Query $query, $field, $value)
      {
           $query->innerJoin(sprintf('%s.%s', $query->getRootAlias(), 'User u'))
                  ->where('u.group_id = ?', $value);
      }
    
  • This method could certainly have gone in the PhonenumberTable class; in which case it would look like this
    public function addGroupIdColumnQuery(Doctrine_Query $query, $field, $value)
      {
          Doctrine::getTable('Phonenumber')->applyGroupIdFilter($query, $value);
      }
    

Uncategorized Don’t let idiots make file extensions…

0 Comments

The thing is, there are like ten of them. That means that ten people thought “hey, you know what would make a great file extension? ASS!”

funnyUnhappy Hour?

0 Comments

OS X Make safari open links in new tab

0 Comments

To force safari to open links that would otherwise have been opened in a new window (target=_blank) to open in new tabs instead:

  • defaults write com.apple.Safari TargetedClicksCreateTabs -bool true

OS X Changing screenshot file location in osx

0 Comments

  • $ defaults write com.apple.screencapture location /Users/ashton/Pictures/Screenshots/
  • $ killall -HUP SystemUIServer