Kohana's ORM comes with built in Kohana's Validation.
As much as I understood, it validates fields that will be added to
the database. It won't work for me because I need to validate fields
that come from $_POST (in simple speaking).
Let me give you an example.
In controller:
$data = Arr::extract($this->request->post(), array('username', 'password', 'password_repeatedly', 'email'));
try {
ORM::factory('User')->sign_up($data);
$this->request->redirect('sign-in');
} catch(ORM_Validation_Exception $exception) {
$errors = $exception->errors('error_messages');
echo 'There were errors:<br />';
echo Debug::dump($errors);
exit;
}
Variable $data is array I need to validate. Method sign_up() is
just custom method in my ORM model that will create user. Sorry about
"echo'es" and "exit's" in controller - I'm just debugging...
My ORM model looks like this:
public function rules() {
return array(
'username' => array(
array('not_empty')
),
'hashed_password' => array(
array('not_empty')
),
'email' => array(
array('not_empty')
)
);
}
public function sign_up($post) {
$salt = $this->_hurricane->generate_salt();
$hashed_password =
$this->_hurricane->hash_password($post['password'], $salt);
$this->username = $post['username'];
$this->hashed_password = $hashed_password;
$this->salt = $salt;
$this->email = $post['email'];
$this->save();
}
I want to check that those three elements of variable $data are
NOT empty! As I said, it checks elements before ORM::save() is
called. And if ypu look closer at my code... in my custom method I
have set hashed_password to be something. It will make it hashed.
Problem is that if user haven't submitted any password (I call that
field 'password' in my HTML form, but 'hashed_password' in
database)... if no password is submitted - it will hash empty string
that will lead to hash anyway. So hashed_password is set!
Then validation is turned on by ORM::save() and in conclusion -
password never can be possibly empty! How to deal with this? Extra
validation in controller? How would you deal with it? Maybe a little
bit different logic?
P.S. Any other suggestions to my code will be appreciated. Thanks in advice!
I don't see what is 'wrong' with your current method.
You can add a condition (Model_user::signup()) to check if the requested password is empty before hashing it (ofc, not setting it at all if it is), so it'll remain empty and make validation fail.
One more thing I can notice here is that the signup method itself is ambiguous, it could easily be done using normal create() combined with a filter for password (so that hashed_password and salt are set when it's changed).
Imho it's also a good practice to use conditional rules / filters, depending on the current objects' state.
Related
I'm trying to hash passwords using the CodeIgniter 3 library "Community Auth", and I think I've found that only certain special characters will work. I just can't tell why, or really what's happening to them.
Here's an example password that works: KA83**8!d#
Here's an example password that does NOT work: 1aA!##%^&*()-_=+{};:,<.>
Edit: I should clarify that the passwords save to the DB, but trying to login is what doesn't work.
If I pull the below methods out to a single function that does hash_passwd and password_verify, then a comparison do work for both passwords.
Should I be using preg_quote on the string so that it saves correctly to the database? I tried, but it didn't seem to affect anything.
Here are the two methods that I use to change a password, and then the third to check for login.
Model = application\models\User_model.php
Method = change_password
$this->db->where('user_id', $user_data->user_id)
->update(
$this->db_table('user_table'), [
'passwd' => $this->authentication->hash_passwd($password),
'passwd_recovery_code' => NULL,
'passwd_recovery_date' => NULL
]
);
Model = application\third_party\community_auth\libraries\Authentication.php
Method = hash_passwd
public function hash_passwd($password) {
return password_hash($password, PASSWORD_BCRYPT, ['cost' => 11]);
}
Model = application\third_party\community_auth\libraries\Authentication.php
Method = check_passwd
public function check_passwd($hash, $password) {
if (password_verify($password, $hash)) {
return TRUE;
}
return FALSE;
}
I figured it out. The change_password method is escaping the password, but the login method was not. Updated to escape both, and now it works.
i got a Laravel 5.4 project where i'm trying to run a simple integration test which checks to see if the user can login properly, in order to do this i'm generating, a new user via a Factory.
$user = factory(User::class)->create(['password' =>'secret']);
Then in my Test
$this->visitRoute('admin.login')
->submitForm('LOGIN', ['email' => $user->email, 'password' => 'secret'])
->seeIsAuthenticated()
->seeStatusCode(200);
The user is never authenticated, i can confirm the $user->email matches the one in the DB, but the password never does...
So i checked the hash it generated in the DB with the "secret" string on this website https://www.dailycred.com/article/bcrypt-calculator
I get an error saying invalid salt revision i'm puzzled, what is this all about?
I'm hashing the password via a Mutator, which looks like this
public function setPasswordAttribute($value) {
$this->attributes['password'] = bcrypt($value);
}
As per advice already given in one of the comments below i tried the following solution
$hasher = new BcryptHasher();
$hash = $hasher->make($value);
$this->attributes['password'] = $hash;
Through xDebug i can tell that it enters the make() method twice, once on initialisation before the Mutator is called and it sends a random set of chars at this point, then it will run the mutator and the make() command with the string secret just as expected, i pull the Hash it generates and find the same issue... invalid salt revision
I use this mutator in my User model and the password is hashed automatically only if it needs:
public function setPasswordAttribute($value)
{
if( \Hash::needsRehash($value) ) {
$value = \Hash::make($value);
}
$this->attributes['password'] = $value;
}
I figured out the problem, it was silly, so, in my LoginController i was checking if the user has the active flag set along with the email and password matching...
protected function attemptLogin(Request $request)
{
return Auth::attempt([
'email' => $request->input('email'),
'password' => $request->input('password'),
'active' => 1
]);
}
Basically, my Factory has a 50/50 rule regarding the 'active' flag so anytime the generated user had the active flag = 1 it would work, the other 50% of the time it would fail.
Sorry but thanks for your help everyone, hope this helps someone in the future :)
When I'm updating my model-bound form with
$user->update(Input::all())
My password field is re-hashed, even when it's empty. I have set my User.php class to automatically hash that field, but shouldn't it be skipped since the field is empty?
You could use in this case:
Input::except('password')
so in your controller you could do it this way:
if (trim(Input::get('password')) == '') {
$data = Input::except('password');
}
else {
$data = Input::all();
}
$user->update($data);
However you should consider other possible issues for that. In this case if user send input with id name (and anyone can do it even if you don't have such field in your form) he could change easily other users passwords/accounts and destroy your whole data.
You should use in your User model at least:
protected $guarded = array('id');
to protect user id from being changed during mass assignment but maybe there are also some other fields you would like to protect (you should list them in $guarded array.
For me much better option in this case is using standard user updating:
$user = User::find($id);
if (trim(Input::get('password')) != '') {
$user->password = Hash::make(trim(Input::get('password')));
}
$user->name = Input::get('name');
// and so on - this way you know what you are changing and you won't change something you don't want to change
$user->save();
Just as Tom Bird commented, here's some code for an example.
If you use a mutator like setPasswordAttribute() method in your model then you can do this:
public function setPasswordAttribute($password)
{
if (!empty($password))
{
$this->attributes['password'] = bcrypt($password);
}
}
This will prevent a new password from being hashed. This setPasswordAttribute() method is called a "mutator" and became available in Laravel 4.2 from what I see. http://laravel.com/docs/4.2/eloquent
Because you have sent all of the input to the user model it assumes you want to update all fields including the password even though it is an empty string, it is possible to hash an empty string.
You need to check if the password is empty and if it is use Input::except('password')
The simple method you can use for this is array_filter. Array_filter filter excludes any empty field. so if you password field is empty then it will not be included in the user update model and when the password field is not included it will not be hashed since mutators and accessors only work when the model has the given attribute. When you filter it out the fields, the model does not receive the field and thus will not hash. You use it in following way...
$user->update(array_filter(Input::all()));
or
$user->update(array_filter($request->all()));
The only problem with this is it will not only exclude password but also all the field that were set empty.
public function update($id)
{
$register = Register::findOrFail($id);
if (empty(Request::get('password'))) {
$data = Request::except('password');
} else {
$data = Request::all();
}
$register->update($data);
return redirect('register');
}
So I'm attempting to authenticate my user's using Laravel's custom filters. I have my LDAP PHP script working and I have essentially plugged it in to my custom filter. However, I need to pass this script the username and password that the user enters on the log in screen; in other words, I need to pass my custom filter this username and password from the log in form.
Here is my code to help explain my problem:
routes.php
Route::group(array('before' => 'ldapTest'), function() {
Route::controller('apps', 'AppController', array(
//named routes here
));
});
filters.php
Route::filter('ldapTest', function()
{
$username = //how do I get this?
$password = //how do I get this?
//LDAP logic goes here; assume $ldapConn and $userDN are properly initialized
$userBind = #ldap_bind($ldapConn, $userDN, $password);
if($userBind)
{
Auth::login(//what goes here? I want to access $username later on in applications);
return Redirect::to('apps/home');
}
else
{
echo 'Incorrect password';
}
});
From reading the documentation I understand you can pass parameters as strings to filters like so: Route::filter('ldapTest:400', function(), but I don't understand how I could use this to pass my username and password using what I assume would be Input::get().
How can I do this?
Actually you can pass parameters into your custom filter and in this case your filter should look like this:
Route::filter('ldapTest', function($route, $request, $param){
//...
});
In your Closure the third argument will receive the parameter you passed in and it's $param, so you are able to pass it like this in your before filter:
array('before' => 'ldapTest:someString')
So, in the filter, the $param will contain someString but in your case that would be a bit different I think, because you want to receive the user inputs submitted through a form so to get those inputs you may use something like this in your filter's handler (Closure):
$username = $request->get('username'); // Assumed that username is a form field
$password = $request->get('password');
Also you may use Input::get('username') instead of $request if you want but it'll be working with $request instance variable and I would prefer that to use.
I had a similar needing in my project, solved with this (not so much elegant but working) workaround:
Route::filter('multiParamFilter', function($route, $request, $params){
list($p1, $p2) = explode(':', $params);
//Now you have $p1 and $p2 initialized with parameters
....
}
In routes.php you can call:
Route::get('path', array('before' => 'multiParamFilter:p1:p2' ......
Note: It requires that you dont use ':' (or at least another symbol) in your parameter values
When my users log into the website their first name, last name and ID are missing from the session data because my session data is coded to take post data and submit into the session table in my database.
Because user logs in with email and password in my session data only email appears and nothing else does.
How can I make first name, last name and id appear in my session table in my db? I want to some how grab these details from the database when user is logging in and provide it in my $u_data array so it get's posted upload login success.
Here is my code:
<?php
class Login_Model extends CI_Model {
public function checkLogin() {
$this->db->where('email', $this->input->post('email')); //compare db email to email entered in form
$this->db->where('password', $this->hashed()); //compare db password to hashed user password
$query = $this->db->get('users'); //get the above info from 'user' table
if ($query->num_rows() == 1) { //if number of rows returned is 1
$u_data = array( //new variable with session data
'user_id' => $this->db->insert_id(),
'email' => $this->input->post('email'),
'first_name' => $this->input->post('first_name'),
'last_name' => $this->input->post('last_name'),
'logged_in' => TRUE
);
$this->session->set_userdata($u_data); //send data from variable to db session
return TRUE;
} else {
return FALSE;
}
}
public function hashed() { //hashing method
// sha1 and salt password
$password = $this->encrypt->sha1($this->input->post('password')); //encrypt user password
$salt = $this->config->item('encryption_key'); //grab static salt from config file
$start_hash = sha1($salt . $password);
$end_hash = sha1($password . $salt);
$hashed = sha1($start_hash . $password . $end_hash);
return $hashed;
}
}
If you're tracking sessions in your DB, two solutions come to mind.
First, you could select the first/last from the user table and insert it into the session table. This requires changes to your application.
Second, you could set up a view for your application, in which the session table is automatically joined with the appropriate user, but that assumes you already have some unique identifier for which user it is in the session (was that the email address?*). This solution would not require any changes to the application code, but would require changes to the DB, which may be the preferred method depending upon your deployment requirements (or it may not :) ).
* as a side note, if you're using email addresses for unique identifiers, be aware that some people share email addresses as you decide if this is the right solution for you.
It'd be as simple as doing something like:
session_start();
$_SESSION['userdata'] = $u_data;
within your CheckLogin method. The session is just a regular PHP array that happens to be automatically preserved for you. You can put anything you want into it, but you do have do put things into it yourself - PHP won't do it for you.
comment followup:
gotcha. So, you simply modify you class to fetch that information from the DB once the login's authenticated. I don't know how your DB class works, but instead of merely checking if there's a matching row, fetch the first/last name, using a query something like this:
select firstname, lastname
from users
where email=$email and password=$password
If you get a result row, you know it's a valid login, and then you just retrieve the name data. I have no idea how your db class works, but it shouldn't be too hard to get it to do that.
When I'm working with Auth systems in CodeIgniter I have made it a practice to include the "user" object globally in views, and also globally in my controllers, by fetching the userdata in the constructor, like so...
<?php
class My_Controller extends Controller {
private $the_user; //global var to store current user data
function My_Controller() {
parent::Controller();
$data->the_user = $this->ion_auth->get_user(); //get user data
$this->load->vars($data); //load into all views as $the_user "$the_user"
$this->the_user=$data->the_user; //load into private class variable "$this->the_user"
}
At that point $the_user variable object is available in all views by default AND $this->the_user is always available to controller functions. It always represents the user currently logged in.
I am using Ion_auth for authentication and fetching the user, so that piece you would have to fill in.
I actually just constructed a "How-to" to implement extended Controller classes so all the Auth logic is automatically inherited to all "protected" Controllers.
The following solved this issue for me. I looked at one of my old questions on here and used my common sense.
I made this edit to my code.
if ($query->num_rows() == 1) { //if number of rows returned is 1
$user = $query->result();
$u_data = array( //new variable with session data
'user_id' => $user[0]->id,
'email' => $this->input->post('email'),
'first_name' => $user[0]->first_name,
'last_name' => $user[0]->last_name,
'logged_in' => TRUE
);