I used bake to generate the controller of my users table, now I need to test using PHPUnit and CakePHP 3 if a record has been entered in the database but I can not see if this actually being inserted.
I can not apply this solution: Question
First error:
1) App\Test\TestCase\Controller\UsersControllerTest::testAdd Failed
asserting that '' contains "The user has been saved.".
If I remove the assertion (or change to assertResponseSuccess)
$this->assertResponseContains('The user has been saved.');
in assertEquals the array $result is empty.
add Action in UsersController:
public function add()
{
$user = $this->Users->newEntity();
if ($this->request->is('post')) {
$user = $this->Users->patchEntity($user, $this->request->data);
if ($this->Users->save($user)) {
$this->Flash->success(__('The user has been saved.'));
return $this->redirect(['action' => 'index']);
} else {
$this->Flash->error(__('The user could not be saved. Please, try again.'));
}
}
$userTypes = $this->Users->UserTypes->find('list', ['limit' => 200]);
$this->set(compact('user', 'userTypes'));
$this->set('_serialize', ['user']);
}
testAdd in UsersControllerTest:
public function testAdd()
{
$this->get('/users/add');
$this->assertResponseOk();
//-------------------------------------------------------------------------
$data = [
'id' => 999999,
'email' => 'usuariocomum999999#gmail.com',
'password' => 'usuariocomum999999senha',
'username' => 'usuariocomum999999username',
'user_type_id' => 900000,
'created' => '2014-07-17 18:46:47',
'modified' => '2015-07-17 18:46:47'
];
$this->post('/users/add', $data);
$this->assertResponseContains('The user has been saved.');
//-------------------------------------------------------------------------
$expected = [
[
'id' => 999999,
'email' => 'usuariocomum999999#gmail.com',
'password' => 'usuariocomum999999senha',
'username' => 'usuariocomum999999username',
'user_type_id' => 900000,
'created' => new Time('2014-07-17 18:46:47'),
'modified' => new Time('2015-07-17 18:46:47')
]
];
$users = TableRegistry::get('Users');
$query = $users->find('all', [
'fields' => ['Users.id', 'Users.email', 'Users.password',
'Users.username', 'Users.user_type_id', 'Users.created',
'Users.modified'],
'conditions' => ['Users.id' => 999999]
]);
$result = $query->hydrate(false)->toArray();
$this->assertEquals($expected, $result);
}
Datasource test:
'test' => [
'className' => 'Cake\Database\Connection',
'driver' => 'Cake\Database\Driver\Mysql',
'persistent' => false,
'host' => 'localhost',
//'port' => 'nonstandard_port_number',
'username' => 'shop',
'password' => 'shop',
'database' => 'shoppingtest',
'encoding' => 'utf8',
'timezone' => 'UTC',
'cacheMetadata' => true,
'quoteIdentifiers' => false,
//'init' => ['SET GLOBAL innodb_stats_on_metadata = 0'],
]
Note: CakePHP 3.0.11 and PHPUnit 4.8.6
You need to test that there was a redirect and a success message in the Session, The response will not contain your text as the response is just a redirect header code for the browser.
Related
here is my initialize method in AppController
public function initialize()
{
$this->log('called', LOG_DEBUG);
$this->loadComponent('Flash');
$this->loadComponent('Auth', [
'authenticate' => [
'Form' => [
'fields' => [
'username' => 'email',
'password' => 'password'
],
'userModel' => 'Users'
]
],
'authError' => 'Did you really think you are allowed to see that?',
'storage' => 'Session',
'loginAction' => [
'controller' => 'Users',
'action' => 'login'
]
]);
$this->Auth->allow();
}
here is my login method
public function login()
{
$this->log($this->request->getBody(), LOG_DEBUG);
try {
if ($this->request->is('post')) {
$user = $this->Auth->identify();
if ($user) {
$this->log('valid','debug');
$this->Auth->setUser($user);
}else{
$this->log('invalid','debug');
}
}
} catch (Exception $e) {
$this->log($e, LOG_DEBUG);
}
}
here is my post request from the log in login method
{"username":"test#test.com","password":"Test#123"}
Here
'fields' => [
'username' => 'email',
'password' => 'password'
],
It says "use email field as username and password as password". You are posting username not email
From docs:
fields - The fields to use to identify a user by. You can use keys
username and password to specify your username and password fields
respectively.
Could work with {"email":"test#test.com","password":"Test#123"} but that depends on your Users model mappling.
I started using CakePHP 3.6 and I am having trouble to create the authentication login. The new auth function $this->Auth->identify() always returns false.
My code:
public function initialize()
{
parent::initialize();
$this->loadComponent('RequestHandler', [
'enableBeforeRedirect' => false,
]);
$this->loadComponent('Flash');
$this->loadComponent('Auth', [
'authorize' => ['Controller'],
'loginRedirect' => [
'controller' => 'Users',
'action' => 'index'
],
'logoutRedirect' => [
'controller' => 'Users',
'action' => 'login',
'home'
],
Here is where I set the fields:
'authenticate' => [
'Form' => [
'fields' => ['username' => 'username', 'password' => 'password']
]
]
]);
/*
* Enable the following components for recommended CakePHP security settings.
* see https://book.cakephp.org/3.0/en/controllers/components/security.html
*/
//$this->loadComponent('Security');
//$this->loadComponent('Csrf');
}
class UsersController extends AppController
{
// Other methods..
public function beforeFilter(Event $event)
{
parent::beforeFilter($event);
$this->Auth->allow(['logout']);
}
public function login()
{
if ($this->request->is('post')) {
This always returns false but why?
$user = $this->Auth->identify();
debug($this->request);
debug($user);
die;
if ($user) {
$this->Auth->setUser($user);
return $this->redirect($this->Auth->redirectUrl());
}
$this->Flash->error(__('Invalid username or password, try again'));
}
}
public function logout()
{
return $this->redirect($this->Auth->logout());
}
}
class User extends AppModel {
public $validate = array(
'username' => array(
'required' => array(
'rule' => array('notEmpty'),
'message' => 'A username is required'
)
),
'password' => array(
'required' => array(
'rule' => array('notEmpty'),
'message' => 'A password is required'
)
),
'email' => array(
'email' => array(
'rule' => array('email', true),
'message' => 'Please supply a valid email address.'
),
'required' => array(
'rule' => array('notEmpty'),
'message' => 'A email is required'
)
),
'role' => array(
'valid' => array(
'rule' => array('inList', array('admin')),
'message' => 'Please enter a valid role',
'allowEmpty' => false
)
)
);
public function beforeSave($options = array()) {
if (isset($this->data[$this->alias]['password'])) {
$passwordHasher = new SimplePasswordHasher();
$this->data[$this->alias]['password'] = $passwordHasher->hash(
$this->data[$this->alias]['password']
);
}
return true;
}
}
I followed the tutorials on the cakephp website with zero luck. Help!
Make sure that you test with a hashed password because the Auth component is expecting it to be hashed so this was my bad.
I used bake to generate the controller of my users table, now I need to test using PHPUnit and CakePHP 3 if a record has been deleted.
NO error was prompted, just failure in test.
I can not apply this solution: Solution
testDelete in UsersControllerTest:
public function testDelete()
{
$this->get('/users/delete/900000');
$users = TableRegistry::get('Users');
$query = $users->find('all', [
'fields' => ['Users.id', 'Users.email', 'Users.password',
'Users.username', 'Users.user_type_id', 'Users.created',
'Users.modified'],
'conditions' => ['Users.id' => 900000]
]);
$result = $query->hydrate(false)->toArray();
$this->assertEmpty($result);
}
delete method in UsersController:
public function delete($id = null)
{
$user = $this->Users->get($id);
if ($this->Users->delete($user)) {
$this->Flash->success(__('The user has been deleted.'));
} else {
$this->Flash->error(__('The user could not be deleted. Please, try again.'));
}
return $this->redirect(['action' => 'index']);
}
Datasource test:
'test' => [
'className' => 'Cake\Database\Connection',
'driver' => 'Cake\Database\Driver\Mysql',
'persistent' => false,
'host' => 'localhost',
//'port' => 'nonstandard_port_number',
'username' => 'shop',
'password' => 'shop',
'database' => 'shoppingtest',
'encoding' => 'utf8',
'timezone' => 'UTC',
'cacheMetadata' => true,
'quoteIdentifiers' => false,
//'init' => ['SET GLOBAL innodb_stats_on_metadata = 0'],
]
Note: CakePHP 3.0.11 and PHPUnit 4.8.6
Try that:
$query = $users->find('all', [
'fields' => ['Users.id', 'Users.email', 'Users.password',
'Users.username', 'Users.user_type_id', 'Users.created',
'Users.modified'],
'conditions' => ['Users.id' => 900000]
]);
$result = $query->hydrate(false)->toArray();
$result_count = count($result);
$expected_count = 0;
$this->assertEquals($expected_count, $result_count);
Here's the component declaration in AppController.php>
'Auth' => array(
'authenticate' => array(
'Form' => array(
'userModel' => 'User',
'fields' => array('username' => 'email', 'password' => 'code'),
'scope' => array('activated' => true),
),
),
'loginAction' => array('controller' => 'users', 'action' => 'login'),
'loginRedirect' => array('controller' => 'members', 'action' => 'dashboard', 'admin' => true),
'authError' => 'No Permission',
'logoutRedirect' => array('controller' => 'pages', 'action' => 'home'),
'userScope' => array('User.activated' => true),
),
The login form:
<?= $this->Form->create('User', array('url' => '/users/login', 'class' => 'form-inline'));?>
<div class="form-group">
<?= $this->Form->input('User.email', array(
'div' => false,
'label' => false,
'placeholder' => 'е-пошта',
'class' => 'form-control',
'required' => true,
));?>
<?= $this->Form->input('User.code', array(
'div' => false,
'label' => false,
'placeholder' => 'сериски број',
'class' => 'form-control',
'required' => true,
));?>
<?= $this->Form->button('<i class="fa fa-user"></i>', array('type' => 'submit', 'class' => 'btn btn-primary', 'escape' => false));?>
</div>
<?= $this->Form->end();?>
And a snippet of the login function:
// ...
if($this->request->is('post')) {
if($this->Auth->login()) {
if(isset($this->request->data['User']['token']) && $this->request->data['User']['token']) {
$token = substr(md5(time()), 0, 32);
$this->User->id = $this->Auth->user('id');
$this->User->saveField('token', $token);
$this->Cookie->write('remember_me', $token, false, '1 week');
}
return $this->redirect($this->Auth->loginRedirect);
}
// ...
Now, when I use $this->Auth->login($this->request->data) or $this->Auth->login($this->request->data['User']), it works, but when I use only $this->Auth->login() it doesn't. I can do a workaround by logging in with $this->request->data and then putting the rest of the user data manually to be available afterwards, but I want to know why this happens. Any ideas?
EDIT
So, as Karthik Keyan mentioned hashing, i figured this was the problem. CakePHP was automatically hashing the password (code field) and I didn't want it to. So I made a custom hasher class named NoPasswordHasher as follows:
App::uses('AbstractPasswordHasher', 'Controller/Component/Auth');
class NoPasswordHasher extends AbstractPasswordHasher {
public function hash($password) {
return $password;
}
public function check($password, $hashedPassword) {
return $password == $hashedPassword;
}
}
and used it in the Auth component:
'Auth' => array(
'authenticate' => array(
'Form' => array(
'userModel' => 'User',
'fields' => array('username' => 'email', 'password' => 'code'),
'scope' => array('activated' => true),
'passwordHasher' => 'No',
),
),
It works now. Thank you.
Tell what type of errors can be display for you.Please check can you store the password in HASH (salt) format.
I try to make hashing while changing password correctly , but i cant, always after changing password, it's hashing twice or i don;t know. Please tell me what am i doing wrong.
class User extends AppModel
{
var $validate = array(
'name' => array(
'length' => array(
'rule' => array('minLength', 5),
'message' => 'Please enter your full name (more than 5 chars)',
'required' => true,
),
),
'username' => array(
'length' => array(
'rule' => array('minLength', 5),
'message' => 'Must be more than 5 characters',
'required' => true,
),
'alphanum' => array(
'rule' => 'alphanumeric',
'message' => 'May only contain letters and numbers',
),
'unique' => array(
'rule' => 'isUnique',
'message' => 'Already taken',
),
),
'email' => array(
'email' => array(
'rule' => 'email',
'message' => 'Must be a valid email address',
),
'unique' => array(
'rule' => 'isUnique',
'message' => 'Already taken',
),
),
'password' => array(
'empty' => array(
'rule' => 'notEmpty',
'message' => 'Must not be blank',
'required' => true,
),
),
'password_confirm' => array(
'required' => array(
'rule' => array('equalToField', 'password', true),
'message' => 'The password you entered does not match',
),
'length' => array(
'rule' => array('between', 6, 20),
'message' => 'Use between 6 and 20 characters',
),
'empty' => array(
'rule' => 'notEmpty',
'message' => 'Must not be blank',
),
),
);
function equalToField($array, $field) {
return strcmp($this->data[$this->alias][key($array)], $this->data[$this->alias][$field]) == 0;
}
var $validateChangePassword = array(
'_import' => array('password', 'password_confirm'),
'password_old' => array(
'correct' => array(
'rule' => 'password_old',
'message' => 'Does not match',
'required' => true,
),
'empty' => array(
'rule' => 'notEmpty',
'message' => 'Must not be blank',
),
),
);
function useValidationRules($key)
{
$variable = 'validate' . $key;
$rules = $this->$variable;
if (isset($rules['_import'])) {
foreach ($rules['_import'] as $key) {
$rules[$key] = $this->validate[$key];
}
unset($rules['_import']);
}
$this->validate = $rules;
}
function password_old($data)
{
$password = $this->field('password',
array('User.id' => $this->id));
return $password ===
Security::hash($data['password_old'], null, true);
}
public function beforeSave($options = array()) {
if (isset($this->data[$this->alias]['password'])) {
$this->data[$this->alias]['password'] = AuthComponent::password($this->data[$this- >alias]['password']);
}
return true;
}
And then my controller:
class UsersController extends AppController
{
var $components = array('Email');
/**
* Account details page (change password)
*/
function account()
{
// Set User's ID in model which is needed for validation
$this->User->id = $this->Auth->user('id');
// Load the user (avoid populating $this->data)
$current_user = $this->User->findById($this->User->id);
$this->set('current_user', $current_user);
$this->User->useValidationRules('ChangePassword');
$this->User->validate['password_confirm']['compare']['rule'] =
array('equalToField', 'password', false);
$this->User->set($this->data);
if (!empty($this->data) && $this->User->validates()) {
$password = $this->Auth->password($this->data['User']['password']);
$this->User->saveField('password', $password);
$this->Session->setFlash('Your password has been updated');
$this->redirect(array('action' => 'account'));
}
}
/**
* Registration page for new users
*/
// function register()
// {
// if (!empty($this->data)) {
// $this->User->create();
// if ($this->User->save($this->data)) {
// $this->Session->setFlash(__('Your account has been created.', true));
// $this->redirect('/');
// } else {
// $this->Session->setFlash(__('Your account could not be created.', true));
// }
// }
// }
public function register(){
if($this->request->is('post')){
$this->User->create();
if($this->User->save($this->request->data)){
$this->Session->setFlash(__('Użytkownik został zapisany', 'success'));
$this->redirect(array('controller'=>'ads', 'action'=>'index'));
} else {
$this->Session->setFlash(__('Błąd zapisu'), 'error');
}
}
}
/**
* Log a user out
*/
function logout()
{
return $this->redirect($this->Auth->logout());
}
/**
* Ran directly after the Auth component has executed
*/
function login()
{
// Check for a successful login
if($this->request->is('post')){
if($this->Auth->login()){
$this->User->id = $this->Auth->user('id'); // zapisuje date logowania
$this->User->saveField('lastlogin', date(DATE_ATOM)); // zapisuje date logowania
$this->redirect($this->Auth->redirect());
} else {
$this->Session->setFlash(__('Nieprawidłowy login lub hasło'), 'error');
}
}
}
and the view:
echo $this->Form->create(array('action' => 'account'));
echo $this->Form->input('password_old', array('label' => 'Old password', 'type' => 'password', 'autocomplete' => 'off'));
echo $this->Form->input('password_confirm', array('label' => 'New password', 'type' => 'password', 'autocomplete' => 'off'));
echo $this->Form->input('password', array('label' => 'Re-enter new password', 'type' => 'password', 'autocomplete' => 'off'));
echo $this->Form->end('Update Password');
Change this line in account function in UsersController from
$password = $this->Auth->password($this->data['User']['password']);
to
$password = $this->data['User']['password'];
$this->Auth->password() do the same function as AuthComponent::password() in model.
So your password was hashing twice.