Sf_Guard_User Extended credentials 1


This is a SfGuardUser Plugin hack to extend user management of their own profile and just their profile, not the other users’.

Moreover, this hack allows also to decide in their profile which field they can modify!

Because by default, if you want an user to be able to change his password for example, he can also modify his permission and rights by himself and finally even modify the others’. Which is really stupid by default in my opinion…

Maybe there was an already existing solution, easy and direct, but after hours on Googling I decided to write my own. It was fun.

So…

I assume that you have installed sfGuardPlugin, maybe add the module sf_guard_user_profile to your app and run/build the models, SQL and database. I assume also that you were so fed up, you set the access only to the admin in the security.yml file.

So, let’s start by the beginning and copy the folder SfGuardUser from SfGuardPlugin to YOUR app/backend/module. We are going to work on these files to avoid to modify the plugin itself. Create a folder ‘template’ inside if it doesn’t exist already, we will use it later for the display.

We are going to work on two sides of the application: the save and the display actions

Let’s manage with the save action

in action.class.php of YOUR sfGuardUser, copy this, and try to understand the comments :

class sfGuardUserActions extends autosfGuardUserActions
{
   
    // Found on the net, check if the user in session is the owner of the requested thing
 public function getCredential()
  {
    $this->thing = sfGuardUserPeer::retrieveByPk($this->getRequestParameter('id')); // retrieving the object based on the request parameters
   
    if ($this->getUser()->isOwnerOf($this->thing))
      $this->getUser()->addCredential('owner');
    else
      $this->getUser()->removeCredential('owner');

    // the hijack is over, let the normal flow continue:
    return parent::getCredential();
  } 

  // New Edit method: there are two method to save : if you are admin, and not
  // if you are admin, it the same
  // if you are not it's a custom one in backend/lib/sfGuardUserAdminForm.classlphp
  //    with unset field  avoiding null inserting in the database
  public function executeEdit(sfWebRequest $request)
  {
      $this->sf_guard_user = $this->getRoute()->getObject();
    $this->form = new sfGuardUserAdminForm(sfGuardUserPeer::retrieveByPk($request->getParameter('id')));
 
    if ($request->isMethod('post'))
    {
      $this->form->bind($request->getParameter('sf_guard_user'));
      if ($this->form->isValid())
      {
          if($this->getUser()->hasCredential('admin', false))
        $sf_guard_user = $this->form->saveForNotAdmin();
        else
        $sf_guard_user = $this->form->save();
       
 
        $this->redirect('sf_guard_user/'.$sf_guard_user->getId().'/edit');
      }
    }
  }
 
    // New process method: there are two method to save : if you are admin, and not
  // if you are admin, it the same
  // if you are not it's a custom one in backend/lib/sfGuardUserAdminForm.classlphp
  //    with unset field  avoiding null inserting in the database
  // note: if you check how everything is working, I think that exectuteEdit is useless because calling processForm
  //    But I don't have the time to check now
    protected function processForm(sfWebRequest $request, sfForm $form)
  {
    $form->bind($request->getParameter($form->getName()), $request->getFiles($form->getName()));
    if ($form->isValid())
    {
      $this->getUser()->setFlash('notice', $form->getObject()->isNew() ? 'The item was created successfully.' : 'The item was updated successfully.');

     // $sf_guard_user = $form->save();
        if($this->getUser()->hasCredential('admin'))
        $sf_guard_user = $this->form->save();
        else
        $sf_guard_user = $this->form->saveForNotAdmin();

      $this->dispatcher->notify(new sfEvent($this, 'admin.save_object', array('object' => $sf_guard_user)));

      if ($request->hasParameter('_save_and_add'))
      {
        $this->getUser()->setFlash('notice', $this->getUser()->getFlash('notice').' You can add another one below.');

        $this->redirect('@sf_guard_user_new');
      }
      else
      {
        $this->redirect('@sf_guard_user_edit?id='.$sf_guard_user->getId());
      }
    }
    else
    {
      $this->getUser()->setFlash('error', 'The item has not been saved due to some errors.');
    }
  }

}

As you may have noticed if you read the comments, there saveForNotAdmin function ! in  backend/lib/sfGuardUserAdminForm.class.php
Copy the sfGuardUserAdminForm.class.php from the plug-in to your lib and add this method inside:

public function saveForNotAdmin($con = null)
  {
     // Just unset the fields you don't want the normal user modify by himself
     unset(
      $this['last_login'],
      $this['created_at'],
      $this['is_active'],
      $this['is_super_admin'],
      $this['sf_guard_user_group_list'],
      $this['sf_guard_user_permission_list']
    );
 
    return parent::save($con);
  }

More over, for the most awake ones, you may have also noticed in getCredential a method isOwnerOf, destined to say that if a user is the owner of an object or not. So, define in myUser.class.php these methods:

  public function isOwnerOf($profile) {
      if($profile!=null)
      return $profile->getOwnerId()==$this->getMyId();
     
      else
      return false;
  }
   

public function getMyId(){
    return $this->getAttribute('user_id',null,'sfGuardSecurityUser');
}

$profile is there to mean $thing, and could be from any object as you can understand. So, in conclusion, you have to define in the object who is his owner, and to simplify, what is the id of the owner.
In our case, our user is looking to modify… himself… yes… but no… he is trying to access his password for example. And that means that he is trying to modify the sfGuardUser ! So let’s go in the plugin, to edit the model…

Look at plugins/sfGuardUserPlugin/lib/model/sfGuardUser.php and add:

class sfGuardUser extends PluginsfGuardUser
{
        public function getOwnerId(){
        return $this->getId();
    }
}

Note: I could have check if an exception is raised if $profile variable in isOwnerOf() is not null and doesn’t implement getOwnerId(), but I know where I use this method, and I don’t have the time, feel free to modify.
Ouf, we are done with the save… That was long.

A bit of security

In the security.yml file in SfGuardUser module and sf_guard_user_profile module if you have, don’t forget to add the credentials for the owner:

default:
    is_secure: on
    credentials: [[admin,owner]]

This will allow only the admin and the owner to consult a profile, even with modifying the URL ! Mission half accomplished.

Let’s enjoy the display action now.

What I want to do: display only the field the user is allowed to modify. Easy to do with front end module of backend module but not with sfGuardPlugin. Why ? Because it uses sfPropelGenerator and sfGuardUserAdminForm class and not sfPropelAdminGenerator and other form.
So, in order to achieve our divine aim, we are going to be very very naughty.
Clear your cache (symfony cc in the shell) and go on the page again. Normally you see the fields, but you cannot save them anymore if you are not an admin user. It’s starting to smell nice.

in SfGuardUser module, you can add these credentials to the generator.yml:

      form:
        class: sfGuardUserAdminForm
        display:
          "NONE":                   [username, password, password_again]
          "Permissions and groups": [is_active, is_super_admin, sf_guard_user_group_list, sf_guard_user_permission_list]

        fields:
          username: { credentials: [owner]  }
          password: { credentials: [owner]  }
          password_again:  { credentials: [owner]  }

and if necessary, edit also sf_guard_user_profile module in the same way.

Finally, in the SfGuardUser template folder, edit the _form_field.php file to have after the <?php else: ?> :

<?php if($sf_user->hasCredential($credentials)): ?>
  <div class="<?php echo $class ?><?php $form[$name]->hasError() and print ' errors' ?>">
    <?php echo $form[$name]->renderError() ?>
    <div>
   
      <?php echo $form[$name]->renderLabel($label) ?>

    
   <?php echo $form[$name]->render($attributes instanceof sfOutputEscaper ? $attributes->getRawValue() : $attributes) ?>

      <?php if ($help || $help = $form[$name]->renderHelp()): ?>
        <div class="help"><?php echo __($help, array(), 'messages') ?></div>
      <?php endif; ?>
    </div>
  </div>
<?php endif; ?>

It will display the field only if the viewer has the credentials.

Et voila ! It was long and difficult but quite interesting to understand how this plugin is working.

That’s all for today, hope it will help someone someday.

Fab

Engineer - Web Juggler - Confit de canard
Fab

Latest posts by Fab (see all)


Leave a comment

Your email address will not be published. Required fields are marked *

One thought on “Sf_Guard_User Extended credentials