I want to build forgot password page in cakephp
Here is my code of user controller
<?php
namespace App\Controller;
use App\Controller\AppController;
use Cake\Http\Exception\UnauthorizedException;
use Cake\Mailer\Email;
use Cake\Mailer\Mailer;
use Cake\email\TransportFactory;
use Cake\Auth\DefaultPasswordHasher;
use Cake\Utility\Security;
use Cake\ORM\TableRegistry;
use Cake\Core\InstanceConfigTrait;
/**
Users Controller
#property \App\Model\Table\UsersTable $Users
#method \App\Model\Entity\User[]|\Cake\Datasource\ResultSetInterface paginate($object = null, array $settings = [])
*/
class UsersController extends AppController {
public function beforeFilter(\Cake\Event\Event $event) {
$this->Auth->allow([‘add’, ‘logout’]);
parent::beforeFilter($event);
}
/**
Index method
#return \Cake\Http\Response|null
*/
public function index() {
if ($this->Auth->user(‘role’) != ‘admin’) {
throw new UnauthorizedException(__(‘You are not allowed to access this page’));
}
$users = $this->paginate($this->Users);
$this->set(compact(‘users’));
}
/**
View method
#param string|null $id User id.
#return \Cake\Http\Response|null
#throws \Cake\Datasource\Exception\RecordNotFoundException When record not found.
*/
public function view($id = null) {
$user = $this->Users->get($id, [
‘contain’ => [],
]);
$this->set(‘user’, $user);
}
/**
Add method
#return \Cake\Http\Response|null Redirects on successful add, renders view otherwise.
*/
public function add() {
if ($this->Auth->user(‘role’) != ‘admin’) {
throw new UnauthorizedException((‘You are not allowed to access this page’));
}
$user = $this->Users->newEntity();
if ($this->request->is(‘post’)) {
$user = $this->Users->patchEntity($user, $this->request->getData());
if ($this->Users->save($user)) {
$this->Flash->success((‘The user has been saved.’));
return $this->redirect(['action' => 'index']);
}
$this->Flash->error(__('The user could not be saved. Please, try again.'));
}
$this->set(compact(‘user’));
}
/**
Edit method
#param string|null $id User id.
#return \Cake\Http\Response|null Redirects on successful edit, renders view otherwise.
#throws \Cake\Datasource\Exception\RecordNotFoundException When record not found.
*/
public function edit($id = null) {
if ($this->Auth->user(‘role’) != ‘admin’) {
throw new UnauthorizedException((‘You are not allowed to access this page’));
}
$user = $this->Users->get($id, [
‘contain’ => [],
]);
if ($this->request->is([‘patch’, ‘post’, ‘put’])) {
$user = $this->Users->patchEntity($user, $this->request->getData());
if ($this->Users->save($user)) {
$this->Flash->success((‘The user has been saved.’));
return $this->redirect(['action' => 'index']);
}
$this->Flash->error(__('The user could not be saved. Please, try again.'));
}
$this->set(compact(‘user’));
}
/**
Delete method
#param string|null $id User id.
#return \Cake\Http\Response|null Redirects to index.
#throws \Cake\Datasource\Exception\RecordNotFoundException When record not found.
*/
public function delete($id = null) {
if ($this->Auth->user(‘role’) != ‘admin’) {
throw new UnauthorizedException((‘You are not allowed to access this page’));
}
$this->request->allowMethod([‘post’, ‘delete’]);
$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’]);
}
public function forgotpassword()
{
if ($this->request->is(‘post’)) {
$email = $this->request->getData(‘email’);
$token = Security::hash(Security::randomBytes(25));
$userTable = TableRegistry::get(‘Users’);
if ($email == NULL) {
$this->Flash->error((‘Please insert your email address’));
}
if ($user = $userTable->find(‘all’)->where([‘email’=>$email])->first()) {
$user->token = $token;
if ($userTable->save($user)){
$mailer = new Mailer(‘default’);
$mailer->Transport(‘Smtp’);
$mailer->From([‘noreply[at]codethepixel.com’ => ‘myCake4’])
->setTo($email)
->setEmailFormat(‘html’)
->setSubject(‘Forgot Password Request’)
->deliver(‘Hello
Please click link below to reset your password
Reset Password’);
}
$this->Flash->success(‘Reset password link has been sent to your email (’.$email.’), please check your email’);
}
if ($total = $userTable->find(‘all’)->where([‘email’=>$email])->count()==0) {
$this->Flash->error((‘Email is not registered in system’));
}
}
}
public function resetpassword($token)
{
if($this->request->is(‘post’)){
$hasher = new DefaultPasswordHasher();
$newPass = $hasher->hash($this->request->getData(‘password’));
$userTable = TableRegistry::get(‘Users’);
$user = $userTable->find(‘all’)->where([‘token’=>$token])->first();
$user->password = $newPass;
if ($userTable->save($user)) {
$this->Flash->success(‘Password successfully reset. Please login using your new password’);
return $this->redirect([‘action’=>‘login’]);
}
}
}
public function login() {
if ($this->request->is(‘post’)) {
$user = $this->Auth->identify();
if ($user) {
if ($user[‘is_active’] === 1) {
$users = $this->Users->get($user[‘id’]);
$users->ip_address = $this->request->clientIp();
$users->last_login = date(‘Y-m-d h:i:s’);
if ($this->Users->save($users)) {
$this->Auth->setUser($user);
return $this->redirect($this->Auth->redirectUrl());
} else {
$this->Flash->error((‘Unable to login by your credentials.’));
}
} else {
$this->Flash->error((‘This user not activated, please contact our administrator.’));
}
}
$this->Flash->error(__(‘Invalid username or password, try again’));
}
}
public function logout() {
return $this->redirect($this->Auth->logout());
}
}
After opening forgot password page user enter their email following error received.
Cannot instantiate abstract class Cake\Mailer\Mailer
How do I solve this problem when the user entered their email & reset password will be sent to user's email which will be saved in our database.
Help me please #ndm
In your app.php
use Cake\Mailer\Transport\MailTransport;
.
.
.
'EmailTransport' => [
'default' => [
'className' => MailTransport::class,
/*
* The following keys are used in SMTP transports:
*/
'host' => 'localhost',
'port' => 25,
'timeout' => 30,
'username' => null,
'password' => null,
'client' => null,
'tls' => null,
'url' => env('EMAIL_TRANSPORT_DEFAULT_URL', null),
],
'your_project' => [
'className' => 'Smtp',
'host' => 'your_host',
'port' => XXX,
'timeout' => 30,
'username' => 'your_email',
'password' => 'your_password',
'client' => null,
'tls' => true,
'url' => env('EMAIL_TRANSPORT_DEFAULT_URL', null),
],
],
'Email' => [
'default' => [
'transport' => 'default',
'from' => 'you#localhost',
//'charset' => 'utf-8',
//'headerCharset' => 'utf-8',
],
'your_project' => [
'transport' => 'your_project',
'from' => ['your_email#teste.com' => 'My Site'],
//'charset' => 'utf-8',
//'headerCharset' => 'utf-8',
],
],
Create a folder "Mailer" in src/Mailer. Create an UserMailer.php file
<?php
namespace App\Mailer;
use Cake\Mailer\Mailer;
class UserMailer extends Mailer
{
public static $name = 'User';
public function reset($user)
{
$this->to($user->email)
->profile('your_project')
->emailFormat('html')
->template('your_project', 'user')
->layout('user')
->viewVars(['name' => $user->name])
->subject(sprintf('Welcome...', $user->name))
->message("texte...");
}
}
In this case, I created a layout for my email. Create an "Email" folder in the layout.
On your controller, do the following:
$this->getMailer('User')->send('reset', [$user]);
Don't forget to import into the controller:
use Cake\Mailer\MailerAwareTrait;
I use it like this.
Go to the godaddy dashboard and find the SMTP information. It's all you need! Replace with your information
Related
I am trying to implement auth login and register cakephp 3.7.4
I have used following code for UsersController add method
public function add()
{
$this->viewBuilder()->setLayout('login');
$user = $this->Users->newEntity();
if ($this->request->is('post')) {
$post = $this->request->getData();
$post['created'] = date('Y-m-d H:i:s');
$post['modified'] = date('Y-m-d H:i:s');
$user = $this->Users->patchEntity($user, $post);
if ($this->Users->save($user)) {
$this->Flash->success(__('The user has been saved.'));
return $this->redirect(['action' => 'login']);
}
$this->Flash->error(__('Unable to add the user.'));
}
$this->set('user', $user);
}
But it can't save password in Hash Format
I have also create entity and used this function but it also not helped me
class User extends Entity
{
protected $_accessible = [
'email' => true,
'password' => true
];
protected $_hidden = [
'password'
];
protected function _setPassword($password){
return(new DefaultPasswordHasher)->hash($password);
}
}
I installed a fresh Cake 3.7.4 installation, baked a users table, users controller and users table/entity. The following code for the User Class successfully hashed the password before it is written to the database. Please compare it with your code.
namespace App\Model\Entity;
use Cake\Auth\DefaultPasswordHasher;
use Cake\ORM\Entity;
class User extends Entity
{
protected $_accessible = [
'email' => true,
'password' => true,
'modified' => true,
'created' => true
];
protected $_hidden = [
'password'
];
protected function _setPassword($password)
{
return (new DefaultPasswordHasher())->hash($password);
}
}
Just use parenthesis around DefaultPasswordHaser():
return(new DefaultPasswordHasher())->hash($password);
Am using cakephp 3 and i want to create login sistem on my app. I follow blog tutorial for auth. When i create new user password is stored as plain text, its not hashed. I dont know what do wrong. Check my code
I create table:
CREATE TABLE users (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50),
PASSWORD VARCHAR(255),
role VARCHAR(20),
created DATETIME DEFAULT NULL,
modified DATETIME DEFAULT NULL
);
Also i create UsersTable and entity Users
class UsersTable extends Table
{
/**
* Initialize method
*
* #param array $config
*/
public function initialize(array $config)
{
$this->table('users');
$this->primaryKey('id');
}
}
class User extends Entity
{
protected $_accessible = [
'username' => true,
'password' => true
];
protected function _setPassword($password) {
return (new DefaultPasswordHasher)->hash($password);
}
}
// App\Controller\AppController.php
$this->loadComponent('Auth', [
'loginRedirect' => [
'controller' => 'Users',
'action' => 'index'
],
'logoutRedirect' => [
'controller' => 'Pages',
'action' => 'display',
'home'
]
]);
And inside UsersController i add:
public function beforeFilter(Event $event)
{
parent::beforeFilter($event);
$this->Auth->allow('add', 'logout');
}
// Login
public function login()
{
if ($this->request->is('post')) {
$user = $this->Auth->identify();
if ($user) {
$this->Auth->setUser($user);
return $this->redirect($this->Auth->redirectUrl());
}
$this->Flash->error(__('Invalid username or password, try again'));
}
}
// Add
public function add() {
$user = $this->Users->newEntity($this->request->data);
if ($this->request->is('post')) {
if ($this->Users->save($user)) {
$this->Flash->success(__('The user has been saved.'));
return $this->redirect(['action' => 'index']);
}
$this->Flash->error(__('Unable to add the user.'));
}
$this->set(compact('user'));
}
I am using cakephp3. I have a simple web application that has users. I have two user types, Admin and Standard. I want to limit Standard user to only view and index data. Whereas Admin type user should be able to add, edit, delete, view and index user data.
Basically, I want to globally restrict standard users, so that they cannot access the add, edit and delete methods of the controller.
So can anyone help me achieve it?
Below is /src/Controller/UsersController.php
<?php
namespace App\Controller;
use App\Controller\AppController;
/**
* Users Controller
*
* #property \App\Model\Table\UsersTable $Users
*/
class UsersController extends AppController
{
/**
* Index method
*
* #return void
*/
public function index()
{
$this->paginate = [
'contain' => ['Countries', 'Cities', 'UserGroups', 'UserLevels']
];
$this->set('users', $this->paginate($this->Users));
$this->set('_serialize', ['users']);
}
public function dashboard()
{
}
/**
* View method
*
* #param string|null $id User id.
* #return void
* #throws \Cake\Network\Exception\NotFoundException When record not found.
*/
public function view($id = null)
{
$user = $this->Users->get($id, [
'contain' => ['Countries', 'Cities', 'UserGroups', 'UserLevels', 'Alerts', 'DeviceLogs', 'Devices']
]);
$this->set('user', $user);
$this->set('_serialize', ['user']);
}
/**
* Add method
*
* #return void Redirects on successful add, renders view otherwise.
*/
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.'));
}
}
$countries = $this->Users->Countries->find('list', ['limit' => 200]);
$cities = $this->Users->Cities->find('list', ['limit' => 200]);
$userGroups = $this->Users->UserGroups->find('list', ['limit' => 200]);
$userLevels = $this->Users->UserLevels->find('list', ['limit' => 200]);
$this->set(compact('user', 'countries', 'cities', 'userGroups', 'userLevels'));
$this->set('_serialize', ['user']);
}
/**
* Edit method
*
* #param string|null $id User id.
* #return void Redirects on successful edit, renders view otherwise.
* #throws \Cake\Network\Exception\NotFoundException When record not found.
*/
public function edit($id = null)
{
$user = $this->Users->get($id, [
'contain' => []
]);
if ($this->request->is(['patch', 'post', 'put'])) {
$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.'));
}
}
$countries = $this->Users->Countries->find('list', ['limit' => 200]);
$cities = $this->Users->Cities->find('list', ['limit' => 200]);
$userGroups = $this->Users->UserGroups->find('list', ['limit' => 200]);
$userLevels = $this->Users->UserLevels->find('list', ['limit' => 200]);
$this->set(compact('user', 'countries', 'cities', 'userGroups', 'userLevels'));
$this->set('_serialize', ['user']);
}
/**
* Delete method
*
* #param string|null $id User id.
* #return \Cake\Network\Response|null Redirects to index.
* #throws \Cake\Network\Exception\NotFoundException When record not found.
*/
public function delete($id = null)
{
$this->request->allowMethod(['post', 'delete']);
$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']);
}
public function login()
{
if ($this->request->is('post')) {
$user = $this->Auth->identify();
if ($user) {
$this->Auth->setUser($user);
return $this->redirect($this->Auth->redirectUrl());
}
$this->Flash->error('Your username or password is incorrect.');
}
}
public function logout()
{
$this->Flash->success('You are now logged out.');
return $this->redirect($this->Auth->logout());
}
public function resetPassword() {
}
public function changepassword() {
}
}
Below is AppController.php
<?php
namespace App\Controller;
use Cake\Controller\Controller;
use Cake\Event\Event;
class AppController extends Controller
{
use \Crud\Controller\ControllerTrait;
public $components = [
'RequestHandler',
'Crud.Crud' => [
'actions' => [
'Crud.Index',
'Crud.View',
'Crud.Add',
'Crud.Edit',
'Crud.Delete'
],
'listeners' => [
'Crud.Api',
'Crud.ApiPagination',
'Crud.ApiQueryLog'
]
]
];
/**
* Initialization hook method.
*
* Use this method to add common initialization code like loading components.
*
* e.g. `$this->loadComponent('Security');`
*
* #return void
*/
public function initialize()
{
parent::initialize();
$this->loadComponent('RequestHandler');
$this->loadComponent('Flash');
$this->loadComponent('Auth', [
'authenticate' => [
'Form' => [
'fields' => [
'username' => 'email',
'password' => 'password'
]
]
],
'loginAction' => [
'controller' => 'Users',
'action' => 'login'
],
// default is referer and in case of no referer loginRedirect (after login)
'loginRedirect' => [
'controller' => 'Users',
'action' => 'dashboard'
],
'logoutRedirect' => '/',
'authError' => "Y"
]);
// Allow the display action so our pages controller
// continues to work.
$this->Auth->allow(['resetPassword','add','changePassword','display']);
}
/**
* Before render callback.
*
* #param \Cake\Event\Event $event The beforeRender event.
* #return void
*/
public function beforeRender(Event $event)
{
if (!array_key_exists('_serialize', $this->viewVars) &&
in_array($this->response->type(), ['application/json', 'application/xml'])
) {
$this->set('_serialize', true);
}
}
}
Regards,
The right approach is to use ControllerAuthorize.
As described in the docs, you have to add in AppController the following:
public function isAuthorized($user = null)
{
// Any registered user can access public functions
if (empty($this->request->params['prefix'])) {
return true;
}
// Only admins can access admin functions
if ($this->request->params['prefix'] === 'admin') {
return (bool)($user['role'] === 'admin');
}
// Default deny
return false;
}
You then have to define admin as a valid key for prefix routing, and move your add, edit and delete actions to the new controller in
src/Controller/Admin/UsersController.php
I am using the following code to test the login action in UsersController
public function testLogin() {
$data = array('User' => array(
'username' => 'hello',
'password' => '411'
)
);
$this->Users = $this->generate('Users',array('components'=> array('Auth','Session')));
$this->Users->Auth->staticExpects($this->once())
->method('user')
->with('id');
$this->testAction('/users/login', array('data' => $data, 'method' => 'post'));
}
and the fixture is-
class UserFixture extends CakeTestFixture {
public $import = array('model' => 'User', 'records' => true, 'connection' => 'fixture');
}
adn action is-
public function login() {
if($this->request->is('post')) {
if($this->Auth->login()) {
$this->redirect($this->Auth->redirect());
} else {
return false;
$this->Session->setFlash(__('Wrong Username Or Password,Please Try Again'));
}
}
}
It always showing
Expectation failed for method name is equal to when invoked 1 time(s).
Method was expected to be called 1 times, actually called 0 times.
What is the problem?Cant find any solution,and the reason also.Please help.
I think your problem mocking the auth component is that in addition to including auth in your components array, you need to specify which methods of Auth to mock, and how to mock them.
The way I deal with AuthComponent in my tests is I create a superclass with a method: _generateMockWithAuthUserId which mocks the Auth component, among other things, the way I need.
I've pasted the code from my superclass below.
class AppControllerTest extends ControllerTestCase {
public function setUp() {
parent::setUp();
$this->User = ClassRegistry::init('User');
}
public function tearDown() {
unset($this->User);
parent::tearDown();
}
public function testPlaceholder(){
// This just here so we don't get "Failed - no tests found in class AppControllerTest"
$this->assertTrue(true);
}
protected function _generateMockWithAuthUserId($contollerName, $UserId){
$this->authUserId = $UserId;
$this->authUser = $this->User->findById($this->authUserId);
$this->controller = $this->generate($contollerName, array(
'methods' => array(
'_tryRememberMeLogin',
'_checkSignUpProgress'
),
'components' => array(
'Auth' => array(
'user',
'loggedIn',
),
'Security' => array(
'_validateCsrf',
),
'Session',
)
));
$this->controller->Auth
->expects($this->any())
->method('loggedIn')
->will($this->returnValue(true));
$this->controller->Auth
->staticExpects($this->any())
->method('user')
->will($this->returnCallback(array($this, 'authUserCallback')));
}
public function authUserCallback($param){
if(empty($param)){
return $this->authUser['User'];
} else {
return $this->authUser['User'][$param];
}
}
}
And then here's a example of a class that inherits from that superclass. Take note of where/how it calls _generateMockWithAuthUserId. Basically, doing that sets up a suitable controller with Auth mocked for the appropriate user id.
<?php
require_once dirname(__FILE__) . DS . 'AppControllerTest.php';
class EmployeeNotesControllerTestCase extends AppControllerTest {
public $fixtures = array(
// your fixtures go here
);
public function setUp() {
parent::setUp();
$this->EmployeeNote = ClassRegistry::init('EmployeeNote');
}
public function tearDown() {
unset($this->EmployeeNote);
parent::tearDown();
}
public function testSupervisorIndexCanNotSeeNotesOnSelf() {
$authUserId = 1;
$this->_generateMockWithAuthUserId('EmployeeNotes', $authUserId);
$this->controller->Session
->expects($this->once())
->method('setFlash');
$result = $this->testAction('supervisor/employee_notes/index/'.$authUserId, array('return' => 'vars', 'method' => 'get'));
$this->assertTrue(empty($result['employeeNotes']));
}
}
Hope that helps.
I have found a solution.it worked.
public function testLogin() {
$data = array('User' => array(
'username' => 'sasa',
'password' => '111'
)
);
$this->Users = $this->generate('Users', array());
$result = $this->testAction('/users/login', array('data' => $data, 'method' => 'post'));
$this->assertEquals($data['User']['username'],$this->Users->Session->read('Auth.User.username'));
$result = $this->testAction('/users/logout');
}
I am trying to make a login by getting/authorizing only one input *user_number* (Not username - password).
I made my current login page with the following way:
Cakephp2.x simple login
Any help plz!
Keep it simple
If you only have one way of identifying users, the simplest (and therefore recommended) way to identify users would be to define your own login function. e.g.:
public function login() {
if ($this->request->is('post')) {
$number = $this->request->data['User']['user_number'];
$user = $this->User->findByUserNumber($number);
if ($user && $this->Auth->login($user)) {
return $this->redirect($this->Auth->redirectUrl());
} else {
$this->Session->setFlash(__('User %d doesn\'t exist', $number), 'default', array(), 'auth');
}
}
}
Note that this varies very little from the standard way of logging a user in with Cake 2.x
Create a Custom Authentication object
Create a Custom Authentication object that authenticates uses by user-number only;
Creating Custom Authentication objects
app/Controller/Component/Auth/UserNumberAuthenticate.php
App::uses('BaseAuthenticate', 'Controller/Component/Auth');
class UserNumberAuthenticate extends BaseAuthenticate {
public function authenticate(CakeRequest $request, CakeResponse $response) {
$userModel = $this->settings['userModel'];
list($plugin, $model) = pluginSplit($userModel);
$fields = $this->settings['fields'];
if (
empty($request->data[$model])
|| empty($request->data[$model][$fields['username']])
) {
return false;
}
return $this->_findUser($request->data[$model][$fields['username']]);
}
/**
* Find a user record via his user-number/identifier
*
* #param string $usernumber The user-number/identifier.
* #return Mixed Either false on failure, or an array of user data.
*/
protected function _findUser($usernumber) {
$userModel = $this->settings['userModel'];
list($plugin, $model) = pluginSplit($userModel);
$fields = $this->settings['fields'];
$conditions = array(
$model . '.' . $fields['username'] => $usernumber,
);
if (!empty($this->settings['scope'])) {
$conditions = array_merge($conditions, $this->settings['scope']);
}
$result = ClassRegistry::init($userModel)->find('first', array(
'conditions' => $conditions,
'recursive' => $this->settings['recursive'],
'contain' => $this->settings['contain'],
));
if (empty($result) || empty($result[$model])) {
return false;
}
$user = $result[$model];
unset($result[$model]);
return array_merge($user, $result);
}
}
Then specify that you want to use your custom authentication object
Inside your AppController:
public $components = array(
'Auth' => array(
'authenticate' => array(
'UserNumber' => array(
'userModel' => 'User',
'fields' => array('username' => 'user_number')
)
)
)
);