I'm working on some admin functions to my site where a user with admin privileges is able to open a form for a given user ID. From this form they can change user permissions, alter the user password or other means.
However, when the form is constructed from the model, the password field pulls the hashed password from the database and populates the password field. As a result, when the admin goes to save the form the hashed password is treated as a plaintext and therefore hashed again, overwriting the original password.
What I need is some way to allow admin users to change the form but only have passwords hashed and updated on the database in the event that it is changed.
My thoughts are to construct the form setting password to blank:
view/User/edit.ctp:
echo $this->Form->input('User.password',array(
'value' => '',
'type' => 'password',
'autocomplete' => 'off'
)
);
And have some sort of check on the save to skip the password; but this is where I'm stuck.
controller/userscontroller.php
public function edit($id = null)
{
$this->User->id = $id;
if ($this->request->is('get'))
{
$this->request->data = $this->User->read();
} else {
//Something here????
if ($this->User->save($this->data))
{
$this->Session->setFlash('Your user has been updated.');
$this->redirect(array('action' => 'index'));
} else
{
$this->Session->setFlash('Unable to update your user.');
}
}
$this->set('groups',$this->User->Group->find('list'));//, array( 'fields' => array('id', 'name'))));
$this->set('sites',$this->User->Site->find('list'));//, array( 'fields' => array('id', 'name'))));
}
How do I check this and prevent the password from updating when there is no change?
Decided Solution:
As per the answers provided I used a second form on the same page that re-uses the signup validation that users go through. When updating site/group privileges the users are sent through one form while passwords through another.
I would build two admin forms, one for changing permissions and the other for updating the password only. While you are at it, the change password form should have a second field for validating the change.
There are some CakePHP plugins to help with managing users and specifically passwords.
I always create a new form specially for changing passwords. You should replace the password field with a link to change the password.
Alternatively, you could disable the input field and require a button to click and use javascript to remove the disabled attribute on the input element
echo $this->Form->input('User.password',array(
'value' => '',
'type' => 'password',
'autocomplete' => 'off',
'disabled' => true
)
);
Jquery because it's easy
$(function(){
$('UsersPassword').click(function(){
$(this).attr('disabled', '0');
});
});
Related
I have a User controller and User model. This model and associated database table is used for authentication and naturally there's a password field.
In my edit action when I call $this->data is puts the hashed password in my password field in my edit view. Naturally, I don't want a password field with a 40-character value, which will then get re-hashed upon save.
My action looks like this:
function edit($id) {
$this->User->id = $id;
if (empty($this->data)) {
$this->data = $this->User->read();
}
else {
if ($this->User->save($this->data)) {
$this->Session->setFlash('User has been updated.');
$this->redirect(array('action' => 'view', $this->User->id));
}
}
}
And my view looks like this:
<h2>Edit User</h2>
<?php
echo $this->Form->create('User', array('action' => 'edit'));
echo $this->Form->input('username');
echo $this->Form->input('password');
echo $this->Form->input('first_name');
echo $this->Form->input('last_name');
echo $this->Form->input('email');
echo $this->Form->end('Save User');
?>
How can I have a form for users to edit their account (username etc) that doesn't update the password if left blank, but does update it if the user enters a new password into the password field?
The common & most secure method is to have a separate form for changing the password where you ask the user to confirm the change by asking the old password. This is because if you forget the site open on a public computer, the next person who comes in can't just hijack the account by just giving a new password.
If you still want to go with the original plan, you can unset the variable if it's empty before saving the data:
if( $this->data[ 'User' ][ 'password' ] == '' ) {
unset( $this->data[ 'User' ][ 'password' ] );
}
The other option is to list allowed fields as a parameter to save() and leave out password if the field is empty.
In the view you can use
echo $this->Form->input('password', array( 'value' => '' ) );
to keep the hash from showing up in the field.
Most places only allow editing passwords when they enter their original passwords. It helps protect against password changes with unauthorized access.
But if you want to do it, try
echo $this->Form->input('password', array('default'=>false));
I am trying to implement facebook Connect to my cakephp Application. i am using Nick's Facebook Plugin.
I wanna implement it this way
When a user Visits the Site he should be able to login via Registration on the site or Facebook Connect
Existing users should be able to connect their account to their FB account
People who first time login to the site using FB Connect and dont have an account on the site. should be redirected to a page where they have to enter details to complete the profile.
What i have done -
I have followed the instruction of Nick to implement it and when i click Login - it connects to my app. but i dont understand how to create a username and password associated with the Fb Connect Id. and user it against the FB token.
Apparently I'm doing the same thing a little before you... ;-)
Here's a method for Facebook login I'm using (slightly redacted and annotated):
public function facebook($authorize = null) {
App::import('Lib', 'Facebook.FB');
$Fb = new FB();
$session = $Fb->getSession();
// not logged into Facebook and not a callback either,
// sending user over to Facebook to log in
if (!$session && !$authorize) {
$params = array(
'req_perms' => /* the permissions you require */,
'next' => Router::url(array('action' => 'facebook', 'authorize'), true),
'cancel_url' => Router::url(array('action' => 'login'), true)
);
$this->redirect($Fb->getLoginUrl($params));
}
// user is coming back from Facebook login,
// assume we have a valid Facebook session
$userInfo = $Fb->api('/me');
if (!$userInfo) {
// nope, login failed or something went wrong, aborting
$this->Session->setFlash('Facebook login failed');
$this->redirect(array('action' => 'login'));
}
$user = array(
'User' => array(
'firstname' => $userInfo['first_name'],
'lastname' => $userInfo['last_name'],
'username' => trim(parse_url($userInfo['link'], PHP_URL_PATH), '/'),
'email' => $userInfo['email'],
'email_validated' => $userInfo['verified']
),
'Oauth' => array(
'provider' => 'facebook',
'provider_uid' => $userInfo['id']
)
);
$this->oauthLogin($user);
}
This gives me an array with all the user details I could grab from Facebook and invokes ::oauthLogin, which either logs the user in with the given information or asks the user to fill in missing details and/or creates a new user record in the database. The most important part you get from the Facebook API is the $userInfo['id'] and/or email address, either of which you can use to identify the user in your database. If you're using the AuthComponent, you can "manually" log in the user using $this->Auth->login($user_id), where $user_id is the id of the user in your own database.
private function oauthLogin($data) {
$this->User->create();
// do we already know about these credentials?
$oauth = $this->User->Oauth->find('first', array('conditions' => $data['Oauth']));
if ($oauth) {
// yes we do, let's try to log this user in
if (empty($oauth['User']['id']) || !$this->Auth->login($oauth['User']['id'])) {
$this->Session->setFlash('Login failed');
}
$this->redirect('/');
}
// no we don't, let's see if we know this email address already
if (!empty($data['User']['email'])) {
$user = $this->User->find('first', array('conditions' => array('email' => $data['User']['email'])));
if ($user) {
// yes we do! let's store all data in the session
// and ask the user to associate his accounts
$data['User'] = array_merge($data['User'], $user['User']);
$data['Oauth']['user_id'] = $user['User']['id'];
$this->Session->write('Oauth.associate_accounts', $data);
$this->redirect(array('action' => 'oauth_associate_accounts'));
}
}
// no, this is a new user, let's ask him to register
$this->Session->write('Oauth.register', $data);
$this->redirect(array('action' => 'oauth_register'));
}
Look no further. Here is an excellent article that'll guide you all the way through (minus any readymade plugins):
Integrating Facebook Connect with CakePHP's Auth component
Simply follow the approach described in there.
Cheers,
m^e
I am using the registration form for different users? After a new user logs in, the registered users should redirect to an after-login page. We are using Auth component for the authentication.
How do I do this?
If you want the user to auto-login after registering, you can use the AuthComponent's login() method.
if ($this->User->save($this->data)) {
$this->Auth->login($this->data);
}
On newer Cakes, you only need to add
$this->Auth->login();
after you add the user into the database.
I'm not sure what the question is, but it sounds like you're wondering how to send a user somewhere after a successful login. If that's correct, try this:
$this->Auth->loginAction = array (
'controller' => 'whichever_controller',
'action' => 'desired_action',
'admin' => true
);
The admin key may not be necessary if you're not accessing /admin/whichever_controller/desired_action.
You will have to call the login method manually from your register action.
Save the username + unhashed password in an array then call it from the method after the save like this:
$data = array('username' => 'user', 'password' => $unhashedPw);
$this->User->login($data);
I'm trying to display custom messages like, 'this field should not be empty' or 'name not null' using the $validate array in the model. I have two controllers, main and users.
The index file of the main controller has the login and registration views. The action part of the login and register functions are in the user_controller. If the login and register function validate, they are redirected to the home page of the main controller,else they remain in the index page itself.
I want the validation messages to be displayed in the index page itself. But those messages appear only if there is a separate view file for login and register,i.e, /views/forms/register.ctp and /views/forms/login.ctp exist.
Is there a way to display those validation messages without having a separate view file for those functions? I have given my code below.Someone guide me please.
Model Class:
<?php
class User extends AppModel {
var $name = 'User';
var $components=array('Auth');
var $validate = array(
'name' => array(
'rule' => 'notEmpty',
'message' =>'Name cannot be null.'
),
'password' => array(
'rule' => 'notEmpty'
),
'email_id' => array(
'rule' => 'notEmpty'
)
);
function registerUser($data)
{
if (!empty($data))
{
$this->data['User']['name']=$data['User']['name'];
$this->data['User']['email_id']=$data['User']['email_id'];
$this->data['User']['password']=$data['User']['password'];
$existingUsers= $this->find('all');
foreach($existingUsers as $existingUser):
if($this->data['User']['email_id']==$existingUser['User']['email_id']){
return 0;
}
else{
$this->save($this->data);
$this->data['User']['id']= $this->find('all',array('fields' => array('User.id'),
'order' => 'User.id DESC'
));
$userId=$this->data['User']['id'][0]['User']['id'];
return $userId;
}
endforeach;
}
}
function loginUser($data)
{
$this->data['User']['email_id']=$data['User']['email_id'];
$this->data['User']['password']=$data['User']['password'];
$login=$this->find('all');
foreach($login as $form):
if($this->data['User']['email_id']==$form['User']['email_id'] && $this->data['User']['password']==$form['User']['password'])
{
$this->data['User']['id']= $this->find('all',array('fields' => array('User.id'),
'conditions'=>array('User.email_id'=> $this->data['User']['email_id'],'User.password'=>$this->data['User']['password'])
));
$userId=$this->data['User']['id'][0]['User']['id'];
return $userId;
}
endforeach;
}
}
?>
Controller Class:
<?php
class UsersController extends AppController
{
var $name = 'Users';
var $uses=array('Form','User','Attribute','Result');
var $helpers=array('Html','Ajax','Javascript','Form');
function register()
{
$this->Session->write('userId',$this->User->registerUser($this->data));
$this->User->data=$this->data;
if (!$this->User->validates())
{
$this->Session->setFlash('Please enter valid inputs');
$this->redirect('/main' );
return;
}
if($this->Session->read('userId')==0){
$this->Session->setFlash('You are already a registerd member.Log in your account');
$this->redirect('/main');
}
else{
$this->Session->setFlash('User account created');
$this->redirect('/main/home');
}
}
function login()
{
//$userId=$this->User->loginUser($this->data);
$this->Session->write('userId',$this->User->loginUser($this->data));
$this->User->data=$this->data;
if (!$this->User->validates())
{
$this->Session->setFlash('Please enter valid inputs');
$this->redirect('/main' );
return;
}
if($this->Session->read('userId')>0){
$this->Session->setFlash('Login Successful');
$this->redirect('/main/home');
break;
}
else{
$this->Session->setFlash('Username and password do not match.');
$this->redirect('/main');
}
}
}
?>
View Template:
<!-- File: /views/main/index.ctp-->
<div id="register">
<h3>Register</h3>
<?php
echo $form->create('User',array('action'=>'register'));
echo $form->input('name');
echo $form->input('email_id');
echo $form->input('password');
echo $form->end('Register');
?>
</div>
<div id="login">
<h3>Login</h3>
<?php
echo $form->create('User',array('action'=>'login'));
echo $form->input('email_id');
echo $form->input('password');
echo $form->end('Login');
?>
</div>
I think you're going about it the wrong way. You're doing way too much in the model, and you're also doing almost the same thing in the controller again after the fact. That's not good. Overall, honestly, the code is quite a mess for something so simple.
A huge WTF flag pops up here:
$existingUsers= $this->find('all');
foreach($existingUsers as $existingUser):
if($this->data['User']['email_id']==$existingUser['User']['email_id']){
You're seriously retrieving all users from the database (potentially a hugely expensive task) and then go through them one by one to compare a single field?!
You can simply define a validation rule that says 'email_id' should be unique, and Cake will automatically ask the database if the 'email_id' already exists. http://book.cakephp.org/view/472/isUnique
About your specific problem: You have the same form field twice on the same page, password and email_id fields for the same User model. There's no way for Cake to know which instance of the two fields is supposed to get the error message, they both have the same name. Also, I don't think you want to use validation error messages for the login form, you just want to see if the login was successful or not. Use Session::flash() instead to display an error message for a failed login, it's not field specific.
Take the login and register methods out of your model, they don't belong there. Only specify proper validation rules for the email, name and password fields in the model, they will automatically be checked upon calling $this->User->save() in the controller.
Don't hand-validate anything, unless there's really no way to do it with Cake validation rules (not the case here). If the built-in validation rules don't satisfy what you need to do, you can even make custom rules. http://book.cakephp.org/view/150/Custom-Validation-Rules
PS: Components are not for models. I think you need to learn more about the basics of Cake before continuing: http://book.cakephp.org/view/218/Tutorials-Examples
But, if you want to see your error messages that comes from the validate array you should access the $this->modelName->invalidFields() which will return you the fields that didn't pass the validation and the message that you have setted for them...
CakePHP's AuthComponent assumes you have a Users table that contains a username and password. I'd like to find a way to override the default tablename from Users to Accounts.
Background Information:
The way I have designed my database is to have a Users table and an Accounts table.
Accounts:
id
user_id
username
password
authentication service (for example, my site, Facebook, Google, OpenID, etc.)
Users:
simply has all the personal information of the user (age, gender, etc.)
The reason for this is so that
each user can have multiple accounts they can login from so they are not locked into one
I can connect the third-party services to an account for more awesomeness
Now back to the problem....
CakePHP has documentation on changing the default field name, but I can't find anything on changing the default table name, but assume it would be similar in nature...
Example of changing the default field name:
function beforeFilter() {
$this->Auth->fields = array(
'username' => 'username',
'password' => 'secretword'
);
}
Is there a way to accomplish this or should I restructure the tables keeping with CakePHP convention and still accomplish the same thing?
In app_controller.php:
function beforeFilter() {
$this->Auth->userModel = 'Account';
}
Below code was helpful in my case as accepting username in email field, you can also define password hash in options.
$this->Auth->authenticate = array(
'Basic' => array('userModel' => 'Account'),
'Form' => array(
'fields' => array('username' => 'email'),
'userModel' => 'Account'
)
);