Pregunta

I have a working example of Auth set up with Users & Groups, based on the cookbook tutorial. I have an additional Locations table, which is associated with my Groups. Location hasMany Group. Group belongsTo Locations. In 2.2 I think I should be able to get location data back for a User, but am not able to.

// App controller
public function beforeFilter() {

    $this->Auth->authenticate = array(
        'all' => array (
            'scope' => array('User.status' => 1)
        ),
        'Form' => array(
                'contain'   => array('Group', 'Location'),
                'fields' => array('Location.name')
        )
  );

The above code only works if I create a direct association between User and Location. Is it possible to use contain here with a Group to Location association?

¿Fue útil?

Solución

Problem of BaseAuthenticate is how it returns user info: return $result[$model];.
So when I need contains I'm using alternative component placed in app/Controller/Auth:

App::uses('FormAuthenticate', 'Controller/Component/Auth');

class FormAndContainableAuthenticate extends FormAuthenticate {

    protected function _findUser($username, $password) {
        if (empty($this->settings['contain'])) {    //< deafult
            $userData = parent::_findUser($username, $password);
        } else {    //< with contains
            $userModel = $this->settings['userModel'];
            list($plugin, $model) = pluginSplit($userModel);
            $fields = $this->settings['fields'];
            $conditions = array(
                $model . '.' . $fields['username'] => $username,
                $model . '.' . $fields['password'] => $this->_password($password),
            );
            if (!empty($this->settings['scope'])) {
                $conditions = array_merge($conditions, $this->settings['scope']);
            }
            $modelObj = ClassRegistry::init($userModel);
            $modelObj->contain($this->settings['contain']);
            $result = $modelObj->find('first', array(
                'conditions' => $conditions
            ));
            if (empty($result) || empty($result[$model])) {
                return false;
            }
            foreach($result as $modelName => $modelData) {
                if ($modelName !== $model) {
                    $result[$model][$modelName] = $modelData;
                }
            }
            $userData = $result[$model];
        }
        // remove dangerous fields like password
        unset($userData[$this->settings['fields']['password']]);
        if (!empty($this->settings['exclude'])) {
            foreach ($this->settings['exclude'] as $fieldName) {
                unset($userData[$fieldName]);
            }
        }
        return $userData;
    }
}

As you can see - it uses parent Component when no contains provided.

Also some bonus: you can provide a set of fields to remove from resulting array. Just pass field names via 'exclude' key

How to use Component:

    public $components = array(
        'Auth' => array(
            'authenticate' => array(
                'FormAndContainable' => array(
                    'fields' => array(
                        'username' => 'username',
                        'password' => 'password',
                    ),
                    'userModel' => 'Staff',
                    'contain' => array('StaffPermission'),
                    'exclude' => array('plain_password')
                )
            ),
        ),
    );

Otros consejos

If User is not linked to Location, but Group is, you should try this:

'contain'   => array('Group' => array('Location')),
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top