I try to find a good and clean way to deal with an admin panel "edit user" in cakePHP v.2.7.
To be clear : I want to be able to edit my user with or without overwriting their password, but the cakePHP validator tool don't let me do what I want...
I've already take a look at CakePHP: Edit Users without changing password and Updating user email and password with CakePHP but it seem really dirty :
the first one don't apply the rules onUpdate
the second display the hash (just no... u_u")
There is no other way to do it ? (with as few line as possible)
TL;DR :
View
// add in your view `app/View/Users/edit.ctp`
// a 'fake' field you'll only use on the controller
echo $this->Form->input('new_password');
Controller
// add in your controller `app/Model/User.php#edit()`
// if we have a new password, create key `password` in data
if(!empty($new_password = $this->request->data['User']['new_password']))
$this->request->data['User']['password'] = $new_password;
else // else, we remove the rules on password
$this->User->validator()->remove('password');
Ok, I finally get what I want, here is my code :
On your app/View/Users/edit.ctp you add a field to your form (a custom one, don't add it to your DB)
<?php
// app/View/Users/edit.ctp
echo $this->Form->create('User');
// your other fields
// a 'fake' field you'll only use on the controller
echo $this->Form->input('new_password');
Don't change your app/Model/User.php ; here is mine :
<?php
// app/Model/User.php
App::uses('AuthComponent', 'Controller/Component');
class User extends AppModel {
public $validate = array(
// [...] other rules
'password' => array(
'passwordLength'=>array(
'rule' => array('minLength', 8),
'message' => 'Too short...',
),
'passwordNotBlank'=>array(
'rule' => 'notBlank',
'required' => true,
'allowEmpty' => false,
'message' => 'A password is required',
),
),
);
public function beforeSave($options = array()) {
if (!empty($pwd = $this->data[$this->alias]['password']))
$this->data[$this->alias]['password'] = AuthComponent::password($pwd);
return true;
}
}
And on your app/Controller/UsersController.php you use this :
<?php
public function edit($id = null) {
$this->User->id = $id;
if (!$this->User->exists())
throw new NotFoundException(__('Invalid user'));
if ($this->request->is('post') || $this->request->is('put')) {
// IMPORTANT >>>>>>>>>>>
// if we have a new password, create key `password` in data
if(!empty($new_password = $this->request->data['User']['new_password']))
$this->request->data['User']['password'] = $new_password;
else // else, we remove the rules on password
$this->User->validator()->remove('password');
// <<<<<<<<<<<<<<<<<<<<<
// then we try to save
if ($this->User->save($this->request->data)) {
$this->Flash->success(__('The user has been updated'));
$this->redirect(array('action' => 'index'));
}
else
$this->Flash->warning(__('The user could not be updated.'));
}
else {
$this->request->data = $this->User->read(null, $id);
unset($this->request->data['User']['password']);
}
}
With the 4 important lines, you are now able to set a new password if needed or disable the validation on the password.
I used this for reference
http://book.cakephp.org/2.0/en/models/data-validation.html#removing-rules-from-the-set
Related
to make it a bit short. I just made a registration form fully working with a controller, the routes and the view. Now I know it's common sense to use a Model for it and in the controller only call the method in the model. So i thought okay lets fix that. Now when I register an account I get a blank page. I bet the redirect is going wrong but I can't fix it maybe you can?
RegisterController.php
public function doRegister(){
$user = new User();
$user->doRegister();
}
User.php (model)
public function doRegister()
{
// process the form here
// create the validation rules ------------------------
$rules = array(
'username' => 'required|unique:users',
'email' => 'required|email|unique:users',
'password' => 'required|min:5',
'serial_key' => 'required|exists:serial_keys,serial_key|unique:users'
);
// create custom validation messages ------------------
$messages = array(
'required' => 'The :attribute is important.',
'email' => 'The :attribute is not a legit e-mail.',
'unique' => 'The :attribute is already taken!'
);
// do the validation ----------------------------------
// validate against the inputs from our form
$validator = Validator::make(Input::all(), $rules);
// check if the validator failed -----------------------
if ($validator->fails()) {
// get the error messages from the validator
$messages = $validator->messages();
// redirect our user back to the form with the errors from the validator
return Redirect::to('/register')
->withErrors($validator)
->withInput(Input::except('password', 'password_confirm'));
} else {
// validation successful ---------------------------
// our duck has passed all tests!
// let him enter the database
// create the data for our duck
$duck = new User;
$duck->username = Input::get('username');
$duck->email = Input::get('email');
$duck->password = Hash::make(Input::get('password'));
$duck->serial_key = Input::get('serial_key');
// save our user
$duck->save();
// redirect with username ----------------------------------------
return Redirect::to('/registered')->withInput(Input::old('username'));
}
}
you need to make $user->doRegister(); a return statement
in your RegisterController you have to do
public function doRegister(){
$user = new User();
return $user->doRegister();
}
try this
return Redirect::to('/registered')
->with('bill_no', Input::get('username'));
in the '/registered' controller,..
use this
$username = Session::get("username");
above worked for me,...
After reading the post: logging without password
I made a personal attempt:
AppController:
function beforeFilter(){
$this->Auth->loginError = "This message shows up when the wrong credentials are
used";
//$this->Auth->authError = "This error shows up with the user tries to access a part
of the website that is protected.";
//$this->Auth->authError = "";
$this->Auth->fields = array(
'username' => 'username',
'password' => null
);
UsersController, inside add():
// Save new user
if ($this->User->save(array('username' => $this->request->data['User']['username'],
'password' => $this->Auth->password(null),
'name' => $this->request->data['User']['name'],
'surname' => $this->request->data['User']['surname'],
'chosenLayout' => $this->request->data['User']['chosenLayout'],
'dateCreated' => $this->request->data['User']['dateCreated'],
'dateModified' => $this->request->data['User']['dateModified'],
'role_id' =>$this->request->data['User']['role_id']
))) {
$this->Session->setFlash(__('message_success_user_added',
array($this->request->data['User']['username'])), 'default', array(), 'success');
$this->redirect(array('action' => 'index'));
}
else {
// Validation error
$this->Session->setFlash(__('message_fail_validation'), 'default', array(), 'fail');
}
Then entered as admin and created some dummy users with null or random password.
Checking the database encrypted passwords were all the same ( a hashed null string) which means the modification in add() function worked...
Inside UsersController login():
// Login User
public function login() {
// Check if the user is already logged in
if ($this->Session->check('Auth.User.id')){
// Redirect to login page
$this->redirect($this->Auth->loginRedirect);
}
else{
// If the user is not logged in
session_set_cookie_params(0);
// If the request is a POST request
if ($this->request->is('post')) {
//get credentials
$this->username = $this->request->data['User']['username'];
$this->password = $this->request->data['User']['password'];
$this->domain = $this->request->data['User']['domain'];
//debug($this->username);
debug($this->domain) ;
//Check if username exists in local DB
//debug($this->User->findByUsername( $this->username ));
if ($this->Auth->login(
array(
'username'=> $this->username,
'password'=> null)
)){
// debug($this->Auth->login(array(
// 'username'=> $this->username,
// 'password'=> null
// )));
// Successful login
// Get all the user information and store in Session
//debug($this->Auth);
$this->User->id = $this->Auth->user('id');
debug($this->User->id);
debug($this->User);
$this->User->contain(array('User', 'Role' => array('Ui', 'Action.name')));
$this->Session->write('User', $this->User->read());
$actions = array();
foreach ($this->Session->read('User.Role.Action') as $key => $value){
array_push($actions, $value['name']);
}
$this->Session->write('User.Role.Action', $actions);
debug($actions);
// Render different layout depending on user type
if($this->Session->read('User.Role.Ui.name') == Configure::read('usertype.msp')){
$this->Session->write('SessionValues.ui', Configure::read('usertype.msp'));
$this->Auth->loginRedirect = array('controller' => 'PortStats', 'action' =>
'index');
}
else if($this->Session->read('User.Role.Ui.name') ==
Configure::read('usertype.tsc')){
$this->Session->write('SessionValues.ui', Configure::read('usertype.tsc'));
$this->Auth->loginRedirect = array('controller' => 'PortStats', 'action' =>
'index');
}
else if($this->Session->read('User.Role.Ui.name') ==
Configure::read('usertype.superAdminUserType')){
$this->Auth->loginRedirect = array('controller' => 'Uis', 'action' => 'index');
}
// Redirect to main login page
$this->redirect($this->Auth->loginRedirect);
}
else {
// Failed login user
session_destroy();
$this->Session->setFlash(__('Login failed:
access not granted'), 'default', array(), 'fail');
}
}
}
}
Then I try to login with my new users.I get the failed login message.
Which means $this->Auth->login returns false.
It must be as easy as that but something is going wrong.
In the meantime my debug trace:
Warning (2): Invalid argument supplied for foreach()
[APP\Controller\UsersController.php, line 85]
Simplify it. It is not a login, but a registration process, so don't confuse those two totally different things.
You just
create the user including proper validation
on success use Auth->login($user['User']) to set the auth session manually
only then redirect manually to where you want the user to go after a registration here
For a live example see https://github.com/dereuromark/cakefest/blob/master/Controller/AccountController.php#L166
Hello everybody i need help on codeigniter roles or permision. i have one user role (the admin) :
Table users ine the database :
id int(11)
email varchar(100)
password varchar(128)
name varchar(100)
in my admin panel i have (page.php controller)=page management, page order, (agent.php controller) = add,edit,delete... , (gyms) = add,edit,delete... ,(article.php controller)
and i have 21 sections, for each section i have more than one treatment, what i want is to assign to each section an admin than can edit and view only his section. so i will have 21 section_admin and one (or more) global_admin than can manage everything
i add an other field in users table named type :
type varchar(50)
it will have two values section_admin or global_admin. I searched but i found no tutorial that shows me how do that.
i don't know how to integrate roles management in my system. Can someone help me?
The controler : user.php
class User extends Admin_Controller
{
public function __construct ()
{
parent::__construct();
}
public function index ()
{
// Fetch all users
$this->data['users'] = $this->user_m->get();
// Load view
$this->data['subview'] = 'admin/user/index';
$this->load->view('admin/_layout_main', $this->data);
}
public function edit ($id = NULL)
{
// Fetch a user or set a new one
if ($id) {
$this->data['user'] = $this->user_m->get($id);
count($this->data['user']) || $this->data['errors'][] = 'User could not be found';
}
else {
$this->data['user'] = $this->user_m->get_new();
}
// Set up the form
$rules = $this->user_m->rules_admin;
$id || $rules['password']['rules'] .= '|required';
$this->form_validation->set_rules($rules);
// Process the form
if ($this->form_validation->run() == TRUE) {
$data = $this->user_m->array_from_post(array('name', 'email', 'password'));
$data['password'] = $this->user_m->hash($data['password']);
$this->user_m->save($data, $id);
redirect('admin/user');
}
// Load the view
$this->data['subview'] = 'admin/user/edit';
$this->load->view('admin/_layout_main', $this->data);
}
public function delete ($id)
{
$this->user_m->delete($id);
redirect('admin/user');
}
public function login ()
{
// Redirect a user if he's already logged in
$dashboard = 'admin/dashboard';
$this->user_m->loggedin() == FALSE || redirect($dashboard);
// Set form
$rules = $this->user_m->rules;
$this->form_validation->set_rules($rules);
// Process form
if ($this->form_validation->run() == TRUE) {
// We can login and redirect
if ($this->user_m->login() == TRUE) {
redirect($dashboard);
}
else {
$this->session->set_flashdata('error', 'That email/password combination does not exist');
redirect('admin/user/login', 'refresh');
}
}
// Load view
$this->data['subview'] = 'admin/user/login';
$this->load->view('admin/_layout_modal', $this->data);
}
public function logout ()
{
$this->user_m->logout();
redirect('admin/user/login');
}
public function _unique_email ($str)
{
// Do NOT validate if email already exists
// UNLESS it's the email for the current user
$id = $this->uri->segment(4);
$this->db->where('email', $this->input->post('email'));
!$id || $this->db->where('id !=', $id);
$user = $this->user_m->get();
if (count($user)) {
$this->form_validation->set_message('_unique_email', '%s should be unique');
return FALSE;
}
return TRUE;
}
}
The model user_m.php :
protected $_table_name = 'users';
protected $_order_by = 'name';
public $rules = array(
'email' => array(
'field' => 'email',
'label' => 'Email',
'rules' => 'trim|required|valid_email|xss_clean'
),
'password' => array(
'field' => 'password',
'label' => 'Password',
'rules' => 'trim|required'
)
);
public $rules_admin = array(
'name' => array(
'field' => 'name',
'label' => 'Name',
'rules' => 'trim|required|xss_clean'
),
'email' => array(
'field' => 'email',
'label' => 'Email',
'rules' => 'trim|required|valid_email|callback__unique_email|xss_clean'
),
'password' => array(
'field' => 'password',
'label' => 'Password',
'rules' => 'trim|matches[password_confirm]'
),
'password_confirm' => array(
'field' => 'password_confirm',
'label' => 'Confirm password',
'rules' => 'trim|matches[password]'
),
);
function __construct ()
{
parent::__construct();
}
public function login ()
{
$user = $this->get_by(array(
'email' => $this->input->post('email'),
'password' => $this->hash($this->input->post('password')),
), TRUE);
if (count($user)) {
// Log in user
$data = array(
'name' => $user->name,
'email' => $user->email,
'id' => $user->id,
'loggedin' => TRUE,
);
$this->session->set_userdata($data);
}
}
public function logout ()
{
$this->session->sess_destroy();
}
public function loggedin ()
{
return (bool) $this->session->userdata('loggedin');
}
public function get_new(){
$user = new stdClass();
$user->name = '';
$user->email = '';
$user->password = '';
return $user;
}
public function hash ($string)
{
return hash('sha512', $string . config_item('encryption_key'));
}
}
There's too many ways how you can incorporate permission system in your project and it all depends what you need. I will give you a basic idea for your case how I would do it IF I understood your question right:
Yes, you can add another field to user table and call it role
To your section table add a user_id field. This is how you connect user with section.
Once user logs in, veryfy if that user is section_user and if yes you need to pull the right section based on that user_id from db.
If not, it means its a global_admin and then display all sections.
I'm not sure if I understood your question right tho.
Let me know.
Save yourself the trouble and use this: Flexi-Auth. You'll have roles and permissions for all the admin types you want for example.
I'm not sure exactly what you're trying to achieve, but I'll explain roughly what I would do:
1) Define a URL scheme
For example if you had a website for car enthusiasts, each brand might be its own section:
somesite.com/section/honda
somesite.com/section/ford
somesite.com/section/toyota
Those URL slugs (honda, ford, toyota etc) effectively become the identifiers for the section you're trying to access. Each one is unique.
You would then want to make sure that each slug after /section/ is a parameter rather than a function call. You can do this by going into /application/config/routes.php and defining a route like this:
$route['section/(:any)'] = section_controller/$1;
// $1 is the placeholder variable for the (:any) regex. So anything that comes after /section will be used as a parameter in the index() function of the section_controller class.
2. Create a new database called 'section', and a corresponding model
For now just give it two fields: *section_id*, and *section_name*. This will store each unique section. The code for the model would be something like this:
class Section extends CI_Model
{
public $section_name;
public $section_id;
public function loadByName($section_name)
{
$query = $this->db->select('section_id', 'section_name')
->from('section')
->where('section_name', $section_name);
$row = $query->row();
$this->section_name = $row->section_name;
$this->section_id = $row->section_id;
return $row;
}
public function loadById($section_id)
{
$query = $this->db->select('section_id', 'section_name')
->from('section')
->where('section_id', $section_id);
$row = $query->row();
$this->section_name = $row->section_name;
$this->section_id = $row->section_id;
return $row;
}
}
3. In the user table, create an additional field called *section_id*
This will be the reference to the ID of the section which they are an admin of. For example if the Toyota section_id is 381, then use 381 as the number in the section_id field in the user table.
4. When the page is requested, look up the section_id based on the slug name.
In your controller file, you should then load the section model somewhere in the index() method like so:
class Section_controller extends CI_Controller
{
public function index($section_name)
{
// I will assume you've already loaded your logged in User somewhere
$this->load->model('Section');
$this->Section->loadByName($section_name);
if ($this->User->section_id == $this->Section->section_id)
{
// Render the page with appropriate permissions
}
else
{
// Throw an error
}
}
}
I won't get into any more specifics of doing all of that; you'll have to read the Codeigniter documentation for a grasp on how to handle routes, controllers, DB queries etc.
if you have only 2 roles then it can achieve easily. you know the user is admin or not if user >is admin then it activate all the section where admin has acess. if user is then he won,t able >to gain access.
if you are comfortalbe to use tankauth authentication library if you have enough time to do task then go to tankauth.
you can also use bonfire(HMVC) for user authentication.
I am validating a user login and would like to attach an error message to the form if they the user submits details that do not authenticate.
In FieldSet I can see function setMessages() but this only appears to match and set against an element key.
How can I attach an error message to the form and not to a form element?
The following code is in within the LoginForm class.
public function isValid()
{
$isValid = parent::isValid();
if ($isValid)
{
if ($this->getMapper())
{
$formData = $this->getData();
$isValid = $this->getMapper()->ValidateUandP($formData['userName'], $formData['password']);
}
else
{
// The following is invalid code but demonstrates my intentions
$this->addErrorMessage("Incorrect username and password combination");
}
}
return $isValid;
}
The first example is validating from a database and simply sending back an error message to the form:
//Add this on the action where the form is processed
if (!$result->isValid()) {
$this->renderLoginForm($form, 'Invalid Credentials');
return;
}
This next one is adding simple validation to the form itself:
//If no password is entered then the form will display a warning (there is probably a way of changing what the warning says too, should be easy to find on google :)
$this->addElement('password', 'password', array(
'label' => 'Password: ',
'required' => true,
));
I hope this is of use.
In ZF1: in order to attach an error message to a form - you can create a decorator element for this:
Taken from:
http://mwop.net/blog/165-Login-and-Authentication-with-Zend-Framework.html
class LoginForm extends Zend_Form
{
public function init()
{
// Other Elements ...
// We want to display a 'failed authentication' message if necessary;
// we'll do that with the form 'description', so we need to add that
// decorator.
$this->setDecorators(array(
'FormElements',
array('HtmlTag', array('tag' => 'dl', 'class' => 'zend_form')),
array('Description', array('placement' => 'prepend')),
'Form'
));
}
}
And then as an example in your controller:
// Get our authentication adapter and check credentials
$adapter = $this->getAuthAdapter($form->getValues());
$auth = Zend_Auth::getInstance();
$result = $auth->authenticate($adapter);
if (!$result->isValid()) {
// Invalid credentials
$form->setDescription('Invalid credentials provided');
$this->view->form = $form;
return $this->render('index'); // re-render the login form
}
Unsure if this still works in ZF2
I've noticed that their is many different ways to pass an ID to a form when editing a database entry. So for example for a edit user profile form I have the following code:
function edit($id = null)
{
$this->layout = 'page';
// this line isn't needed?
//$this->User->id = $id;
if (empty($this->data))
{
$this->data = $this->User->read();
}
else
{
if ($this->User->save($this->data))
{
$this->Session->setFlash('Your profile has been updated', 'flash', array('header' => 'Announcement', 'myclass' => 'success'));
$this->redirect(array('controller' => 'users', 'action' => 'view', $id));
}
}
}
Now the function expects an id passing in the url e.g. /users/edit/2 But let's say I wanted it to be something more user friendly like /profile/edit (rewrote by routing) I would no longer be passing in the ID as part of the url. As you can see in my code I have a line I have commented out because it isn't needed?
Also in the form I ALSO Need <?php echo $this->Form->input('id', array('type' => 'hidden')); ?> why?
Basically this is more of what are the options available to me to build various types of edit forms and passing data to the form. And what is the need for the hidden field in the form if the data is being passed either via the URL or some other way
I've also noticed on some sites that they have things like Form Keys and the username stored in meta tags in the page header???
EDIT:
public function beforeFilter()
{
$this->set('authUser', $this->Auth->user());
//
$user = $this->Auth->user();
if (!empty($user))
{
Configure::write('User', $user[$this->Auth->getModel()->alias]);
}
}
public function beforeRender()
{
$user = $this->Auth->user();
if (!empty($user))
{
$user = $user[$this->Auth->getModel()->alias];
}
$this->set(compact('user'));
}
// NEW VERSION
function settings()
{
$this->layout = 'page';
$this->set('title_for_layout', 'Edit Profile');
$this->User->id = $user['id'];
if (empty($this->data))
{
$this->data = $this->User->read();
}
else
{
if ($this->User->save($this->data))
{
$this->Session->setFlash('Your settings have been updated', 'flash', array('myclass' => 'success'));
$this->redirect(array('controller' => 'users', 'action' => 'settings'));
}
}
}
Also in the form I ALSO Need Form->input('id',
array('type' => 'hidden')); ?> why?
Having the id hidden in the form removes the need for your controller action to grab the $id from the uri (aka passed as parameter). When in the form, it will automatically be placed into your $data array.
what is the need for the hidden field
in the form if the data is being
passed either via the URL or some
other way
It's not needed in the form if it's available from the uri. You'd simply grab the $id and assign it to the User model (as the commented out code does).
let's say I wanted it to be something
more user friendly like /profile/edit
I assume that would be when the user is editing his own profile. In that case, your system should be able to retrieve the user's id via the session.