Include a subset of fields in the Auth User session - cakephp

My Users table has a whole bunch of fields, most of which I don't need/want stored in the Auth User session. How do you restrict which fields are stored in the session for the logged in user?
I know you can choose fields of associated models with the 'contain' key, but normally to select fields of the top-level model, you'd use the 'fields' key. But in the case of Auth, the 'fields' key is used to choose which fields to authenticate the user by, not which fields to include in the session.
To give some context, here's my code so far... what would I do to make it so that only the email and firstname fields are stored in the Auth session, as opposed to all fields in the Users table.
$this->Auth->authenticate = array(
'Blowfish' => array(
'fields' => array(
'username' => 'email',
'password' => 'password',
)
)
);

I've upvoted the answers which were useful, albeit work-around solutions - thanks.
I think the "correct" answer is that there's no way to do this with CakePHP Auth component out of the box, and you have to hack it (eg, using one of the solutions below). I took a look at the _findUser method in BaseAuthenticate.php and it confirms this.
In case a CakePHP core dev is reading (DeEuroMarK?), this is probably a pretty common requirement, and I think it's a feature worth having built in.
Suggested implementation: just include the fields you want as extra fields in the 'fields' array - and just assume that every key other than 'username' and 'password' is an extra field that should be included in the auth session. That way it's consistent with other Model find syntax.
Example:
$this->Auth->authenticate = array(
'Blowfish' => array(
'fields' => array(
'username' => 'email',
'password' => 'password',
'another_field',
'yet_another_field'
)
)
);

in the beforeFilter of my UsersController I have something similar as your login.
Then I set a afterLogin function as the redirect
$this->Auth->loginRedirect = array('controller' => 'users', 'action' => 'afterLogin');
$this->Auth->loginRedirectTrue = array('controller' => 'users', 'action' => 'index');
$this->Auth->logoutRedirect = array('controller' => 'pages', 'action' => 'display');
the login function dus some checks and afterwards redirects to
if ($this->Auth->login()){
// code here
$this->redirect($this->Auth->redirect());
}
and afterLogin function like this
function afterLogin(){
$session = $this->Session->read('Auth');
$user_id = $session['User']['id'];
// change this to find only the fields you need and then override the Auth.User...
$user = $this->User->findById($user_id);
if (!empty($user)){
$this->Session->write('Auth.UserProfile', $user['UserProfile']);
}
$this->redirect($this->Auth->loginRedirectTrue);
}
You should change the findById to suit your needs and then override the Auth.User fields in the session.
Good Luck!

I think the simplest is to add something like this:
add a contain to your Auth-Component configuration
$this->loadComponent('Auth', [
'authorize' => 'Controller',
'loginRedirect' => [
'controller' => 'Users',
'action' => 'index'
],
'logoutRedirect' => [
'controller' => 'Users',
'action' => 'login'
],
'authenticate' => [
'Form' => [
'fields' => ['username' => 'email'],
'contain' => ['Groups']
]
],
'unauthorizedRedirect' => $this->referer()
]);
...
And in you login-action save the user in session:
$foundUser = $this->Auth->identify();
if ($foundUser) {
$this->Auth->setUser($foundUser);
}
...
this will add the containing groups to the Auth.User
The is for CakePhp3 - in older versions it may be different.

Related

cakephp 2.6.1 ajax login not working

Having issues logging in with ajax. Can anyone direct me to some documentation that makes sense. This sounds worrying.
In 2.x $this->Auth->login($this->request->data) will log the user in
with whatever data is posted, whereas in 1.3
$this->Auth->login($this->data) would try to identify the user first
and only log in when successful.
$data['User']['email'] = "this";
$data['User']['password'] = "that";
$data = $this->request->input('json_decode', true);
$this->autoRender = false;
$this->response->type('json');
if ($this->Auth->login($data)){
echo "access";
} else {
echo "access denied";
}
It always prints "access".
In AppController.php
'Auth' => array(
'loginRedirect' => array(
'controller' => 'posts',
'action' => 'index'
),
'logoutRedirect' => array(
'controller' => 'pages',
'action' => 'display',
'home'
),
'authenticate' => array(
'Form' => array(
'fields' => array('username' => 'email'),
'passwordHasher' => 'Blowfish'
),
)
),
Use AuthComponent::identify() instead. But this can (and clearly should) be done better. Your code tells me that you don't have much experience with json in CakePHP.
Check this page of the manual. Also your request should be made with "Accept: application/json" then your data should automatically end up in $this->request->data and then login() should pick it up automatically. The proper way is to send the Accept header and not just rely on the extension, this is general and not just specific to CakePHP.

How can I get the connected route from a URL string in CakePHP?

CakePHP appears to have a function to translate a requested URL and determine what controller and action to perform, seeing this must be performed with each http request.
Is there a way I can utilize this process within a controller or elsewhere in the system? The best outcome would be to have a function where I input a URL string, and the response is an array with controller details. eg:
$url_route = RouteFunction('/page/url/here');
// $url_route = array(
// 'controller' => 'page',
// 'action' => 'display',
// 'pass' => array('url', 'here')
// );
For this you can use Router::parse().
For example:
$route = Router::parse('/users/view/21');
debug($route);
will by default output:
array(
'controller' => 'users',
'action' => 'view',
'named' => array(),
'pass' => array(
(int) 0 => '21'
),
'plugin' => null
)

CakePHP 2.x Auth Condition

In my User Authentication I need to set a Condition (verified = 1) for the Login to happen. I know that I should be able to do it like this:
$this->Auth->userScope = array('User.verified' => '1');
I tried this in AppController and my UsersController beforeFilter function, but it doesn't do anything. Is there anything else I need to configure for this?
I ended up doing (AppController):
public function isAuthorized($user) {
if ($user['verified'] == '0') {
$this->Session->setFlash('You need to verify your Account first.');
return false;
}
return false;
}
This seems to be inelegant, since there should be the proper (userScope) way to do it, plus I now get two Flashes when verified = 0: The first one is the setFlash from above, and the second one is the regular authError.
I checked both, the Docs and stackoverflow, but I found very little information on this topic.
CakePHP 2.x:
public $components = array(
'Auth' => array(
'loginAction' => array(
'controller' => 'users',
'action' => 'login'
),
'authError' => 'Je hebt geen toegang tot dit gedeelte',
'authenticate' => array(
'Form' => array(
'fields' => array('username' => 'email'),
'scope' => array('is_admin' => '1')
),
)
),
'Session'
);
Update: For cakePHP 3.1 finder option is available since 3.1. Prior to that you can use scope and contain options to modify query.
http://book.cakephp.org/3.0/en/controllers/components/authentication.html#customizing-find-query
$this->Auth->authenticate = array(
AuthComponent::ALL => array(
'scope' => array('User.verified' => '1'),
),
);
$this->Auth->authenticate = array(
'Form' => array(
'scope' => array('User.verified' => '1')
)
);
Assuming the CakePHP Documentation is correct Auth::userScope was renamed to Auth::scope so now you would do something like this:
$this->Auth->scope = array ('User.active' => '1');
Configuring Auth in CakePHP 2.x
Hope this helps.
Try this:
$this->Auth->userScope = array('User.verified = 1');

CakePHP - Just find related model data

I have Users and Dashboards. They both have a HABTM relationship with each other. How do I just retrieve all of a user's dashboards, without any of the User data?
I tried this but it returns the User model as well:
$this->User->id = $this->Auth->user('id');
$this->User->find('all', array('contain' => 'Dashboard'));
You can set the fields option so that you retrieve only the id field and none of the others. (You probably can't set it to fetch no fields at all.)
$this->User->find(
'all',
array(
'contain' => 'Dashboard',
'fields' => 'User.id'
)
);
Use the Dashboard model instead with a condition for the user id:
$this->User->Dashboard->bindModel(array('hasMany' => array('DashboardsUsers')));
$this->User->Dashboard->find('all', array(
'conditions' => array('User.id' => $this->Auth->user('id')),
'recursive' => -1
));
I think that should work :)

CakePHP - how to translate current URL after changing language and reloading Routing table

I'm using different routing tables for every language, and I have wrote action which change language and redirects to the same page but in target language (and target url).
The main problem is that my action is way too complicated - how can I make it simple?
It should change language and redirect to new url (in target language).
In short: We had random valid cake url in one language and we had to translate it to adequate url in another language.
My routing table:
if( 'en' == Configure::read('Config.language') ) {
Router::connect('/help', array('controller' => 'pages', 'action' => 'display', 'help') );
} else {
Router::connect('/pomoc', array('controller' => 'pages', 'action' => 'display', 'help') );
}
Action changing language:
function lang($lang) {
// getting previous url table
$url = $this->referer();
$url = Router::parse($url);
// changing language
if( in_array($lang, Configure::read('Languages.valid') ) ) {
$this->Session->write('Language', $lang);
Configure::write('Config.language', $lang);
}
// saving base params
$requestInfo = array(Router::getParams(), Router::getPaths());
// reload routing table
Router::reload();
include(CONFIGS.'routes.php');
// restore base params
Router::setRequestInfo($requestInfo);
// fix for 'pass' params
if(!empty($url['pass']) && is_array($url['pass'])) {
$url = array_merge($url, $url['pass']);
unset($url['pass']);
}
$this->redirect($url);
}
About 'pass' key in url table:
/pages/display/help
after Router::parse(), parameter is extracted:
pass => array(
0 => 'help'
)
and later return value from Router::url() look like that:
/pages/display/pass:Array
so I have to fix it by merging 'pass' value with whole array and removing key
I have my routes.php like this
Configure::write('Config.language', $_SESSION['lang']);
Router::connect(__('/help',true), array('controller' => 'pages', 'action' => 'display', 'help') );
so I use the __() to translate the url. It seaches the translation in the po files. And in your lang() function, after changing the Session to the current lang, all you need to do is:
$this->redirect(__('/help'));
Hope this helps
I know it is too late to answer that, but for those who experience the similar needs:
In routes.php p1 identifies the static page (same for all languages), could be any string that uniquely identifies the page.
Router::connect('/:language/p1/:translation', array(
'controller' => 'pages',
'action' => 'display',
'help',
'options' => array('language' => '[a-zA-Z]{2}')
) );
In your view
$this->Html->link('Click me', array(
'language' => Configure::read('Config.language'),
'controller' => 'pages',
'action' => 'display',
'help',
'translation' => __('help') // could be any string in fact
));
will generate link to /en/p1/help in English, else to /xx/p1/pomoc.
Injection of language parameter into every link could be done in AppHelper::url() instead of providing it in every link occurence.
If you want to redirect in controller:
$this->redirect(array(
'language' => Configure::read('Config.language'),
'controller' => 'pages',
'action' => 'display',
'help',
'translation' => __('help')
));

Resources