I am completing my login function for my application and I'm receiving the trying to get property of non object on a few lines. The first one is shown below that does a function call to the is_user_locked method using the object property lock_date inside the user_data object. I understand that this means that at this point their is no user_data to work with so it can not use the properties. I'm curious to know how should I account for this so that I don't abuse using too many nested if statements.
if (count($user_data) == 0) {
$output = array('content' => 'The user was not found in the database!', 'title' =>
'User Not Found');
}
if ($this->is_user_locked($user_data->lock_date)) {
$output = array('content' => 'This user account is currently locked!', 'title' =>
'Account Locked');
}
Any ideas on why this could be? Any and all suggestions would be helpful.
I'm curious to know how should I account for this so that I don't
abuse using too many nested if statements.
How about something like this ?
if( $user_data ) {
// $user_data contains something so let's proceed
if ($this->is_user_locked($user_data->lock_date)) {
$output = array('content' => 'This user account is currently locked!', 'title' => 'Account Locked');
}
} else {
// Nothing in $user_data so throw Exception or display error
$output = array('content' => 'The user was not found in the database!', 'title' =>
'User Not Found');
}
See this link.
if($var): same as $var == NULL.
What seems to be happening is $user_data->lock_date should be $user_data['lock_date']. I'm not 100% sure by the code you posted but it seems you're just referencing an array element incorrectly.
Terinary operators can be used to avoid if statements
$user_data === null ? "it's null" : "otherwise it's not";
i am a huge fan of negative if checks. the idea is that if you do not get what you expect, you immediately exit. this can be getting back a result, or doing a true false check. Of course you are first going to validate the user name using CI form validation. so for example in your model, the method that checks for the user name - if the result is 0, just have it return false. Then in your controller:
// Validate the user name and other form info
// if validation passes, grab the username,
// note the TRUE, that tells CI to run it through XSS clean
$username = $this->input->post( 'username', TRUE ) ;
// if we did NOT get a user back from model, immediately go to new method
if ( ! $user = $this->users_m->_findUser($username) ) {
$this->_userNotFound(); }
// similar, if the user account is locked, go to new method
// if you return an array from model this would be $user['lock_date']
elseif ($this->isUserLocked($user->lock_date) == TRUE) {
// DO NOT try and write messages etc here. put all that in a separate method
$this->_userAccountLocked(); }
else {
// you have a $user and the user is not locked
// its tempting to write a bunch of stuff here
// do not do that. keep it clean, and go to separate method
$this->_displayAccount($user) ; }
Note that i put an underscore before all the method names - CI will automatically make those private.
Also note the Camel case for method names -- versus underscores. some people prefer it.
You could do this:
if (count($user_data) == 0) {
$output = array('content' => 'The user was not found in the database!', 'title' =>
'User Not Found');
} else if ($this->is_user_locked($user_data->lock_date)) {
$output = array('content' => 'This user account is currently locked!', 'title' =>
'Account Locked');
}
Which is pretty much identical to your initial code, but with an else thrown in.
Related
I'm building a feature in my Laravel 8 API to allow customers to set a Security Question & Security Answer. A customer can set their question via a HTML <select> and set their answer via a standard HTML5 input.
When their answer is saved, I'm hashing it, for security purposes in case data is exposed, however, in my API, I need to somehow decrypt the hashed value for the logged in user so that they can see their answer, and make amendments to it, or change it.
Right now I'm saving the data via a method:
/**
* Update user's security questions
*
* #return Response
*/
public function updateSecurityQuestions(Request $request)
{
// get request data
$request_data = $request->only(['security_question', 'security_answer']);
// validation
$validator = Validator::make($request_data, [
'security_question' => 'nullable|in:1,2,3,4,5,6,7,8',
'security_answer' => 'nullable|max:128'
]);
// if validation fails
if ($validator->fails()) {
$this->error_response['errors'] = $validator->messages();
return response()->json(['success' => false, 'message' => 'It looks like you missed something', $this->error_response], 400);
}
// attempt to save the user
try {
$question = (isset($request_data['security_question']) && !empty($request_data['security_question'])) ? htmlspecialchars($request_data['security_question']) : null;
$answer = (isset($request_data['security_answer']) && !empty($request_data['security_answer'])) ? htmlspecialchars($request_data['security_answer']) : null;
$id = Auth::user();
$user = User::findOrFail($id['id']);
$user->security_question = !$answer ? null : $question;
$user->security_answer = !$question ? null : Hash::make($answer);
$user->save();
if ($user->security_question == null && $user->security_answer == null) {
return response()->json(['success' => true, 'type' => 'warning', 'message' => 'Your security question settings are incomplete'], 200);
}
// everything went okay!
return response()->json(['success' => true, 'type' => 'success', 'message' => 'Your security question and answer has been updated'], 200);
} catch (Exception $e) {
// catch the error
return response()->json(['success' => false, 'message' => 'We was unable to update your security question and answer, please try again later'], 422);
}
}
You can see I'm hashing it, but this value is returned to the front-end via an API in JSON amongst other user data:
{
"security_answer": "$2y$10$gGa6OC34UBzQzMNtVoo.EOj8W9WD5THY68bQME0y6AnRJjYFWWcqO"
}
This is no good because the user sees that value in the input, which obviously isn't good.
Could I not just do something like?
if (Hash::check($user->security_answer, $user->security_answer)) {
// return the answer?
}
What do I do here?
Why you execute save before the if? You need to make it after check if everything's okay
$user->save();
I have set the CI framework with database connection, put it on autoload and created a form, yet still, nothing is inserted into the Database!
I've tried using objects(classes) and different ways to pass information in an array
if (isset($_POST['register-submit'])) {
$this->load->model('Registermodel');
$this->load->library('form_validation');
$this->form_validation->set_rules('register-username', 'Username', 'required');
$this->form_validation->set_rules('register-password', 'Password', 'required|min_length[6]');
$this->form_validation->set_rules('register-password-repeat', 'confirm passphrase', 'required|min_length[6]|matches[register-password]');
$this->form_validation->set_rules('register-pin', 'pin', 'required|regex_match[/^[0-9]{6}$/]');
//If form validation was successful
if ($this->form_validation->run() == TRUE) {
echo 'successfully registered!';
//Add user to database
$data = array(
'ci_useruniqid'=> $_POST['register-uniqid'],
'ci_userdate'=> $_POST['register-date'],
'ci_useruid'=> $_POST['register-username'],
'ci_userpwd'=> password_hash($_POST['register-password'], PASSWORD_DEFAULT),
'ci_usermnemonic'=> $_POST['register-mnemonic'],
'ci_usercurrentaddress'=> $_POST['register-address'],
'ci_useraccount'=> $_POST['register-account'],
'ci_useraccountbalance'=> $_POST['register-account-balance'],
'ci_userpin'=> $_POST['register-pin'],
'ci_userstatus'=> $_POST['register-status'],
'ci_usertype'=> $_POST['register-type'],
'ci_userinfo'=> $_POST['register-info'],
'ci_userpgp'=> $_POST['register-pgp'],
'ci_usercurrency'=> $_POST['register-currency']
);
$this->RegisterModel->adduser($data);
redirect("AuthController/loginview", "refresh");
}
What I expect to happen is for the data(as seen above) to be inserted into the DB. My actual result is no response even something as simple as echoing something out in an if statement.
My table structure:
ci_userid int(11)
ci_useruniqid
ci_userdate date
ci_useruid
ci_userpwd
ci_usermnemonic
ci_usercurrentaddress
ci_useraccount
ci_useraccountbalance decimal(12,8)
ci_userpin
ci_userstatus
ci_usertype
ci_userinfo
ci_userpgp
ci_usercurrency
The rest are text, here is my adduser model:
public function adduser($data) {
$insert = $this->db->insert('users', $data);
}
As this was too long for a comment, I present to you my quasi answer that will help you debug.
echo 'hello world <br><pre>';
print_r($_POST);
if (isset($_POST['register-submit'])) {
$this->load->model('Registermodel');
$this->load->library('form_validation');
$this->form_validation->set_rules('register-username', 'Username', 'required');
$this->form_validation->set_rules('register-password', 'Password', 'required|min_length[6]');
$this->form_validation->set_rules('register-password-repeat', 'confirm passphrase', 'required|min_length[6]|matches[register-password]');
$this->form_validation->set_rules('register-pin', 'pin', 'required|regex_match[/^[0-9]{6}$/]');
//If form validation was successful
if ($this->form_validation->run() == TRUE) {
echo 'successfully registered!';
//Add user to database
$data = array(
'ci_useruniqid' => $_POST['register-uniqid'],
'ci_userdate' => $_POST['register-date'],
'ci_useruid' => $_POST['register-username'],
'ci_userpwd' => password_hash($_POST['register-password'], PASSWORD_DEFAULT),
'ci_usermnemonic' => $_POST['register-mnemonic'],
'ci_usercurrentaddress' => $_POST['register-address'],
'ci_useraccount' => $_POST['register-account'],
'ci_useraccountbalance' => $_POST['register-account-balance'],
'ci_userpin' => $_POST['register-pin'],
'ci_userstatus' => $_POST['register-status'],
'ci_usertype' => $_POST['register-type'],
'ci_userinfo' => $_POST['register-info'],
'ci_userpgp' => $_POST['register-pgp'],
'ci_usercurrency' => $_POST['register-currency']
);
$this->RegisterModel->adduser($data);
echo 'success';
//redirect("AuthController/loginview", "refresh");
} else {
echo validation_errors();
}
} else {
echo 'register-submit... well... does not exist';
}
Please note, use $this->input->post('somename'); for all your $_POST stuff. e.g. assume that register-uniqid doesn't exist (form validation won't catch it because it isn't required) you'll get an undefined index error; thus you'd have to do isset($_POST['register-uniqid']) ? $_POST['register-uniqid'] : null whereas $this->input->post() does that logic for you.
Now, even if you make this fix, if register-uniqid is absolutely critical (cannot be null) then make sure form validation covers it with a required. Even though you may have some hidden fields, it doesn't mean the user can't delete them if they want and post a null to that db column. I would suggest forgoing hidden fields entirely and coding any non-user-related input in to this controller or model.
I wanted to check whether the username exists in the database,
but Codeigniter is throwing an
Error: Can't use method return value in write context.
The code is as follows:
public function check_username_exists($username){
$query = $this->db->get_where('users', array('username' => $username));
if(empty($query->row_array())){
return true;
}else{
return false;
}
}
$result = $query->row_array();
if(empty($result)){
return true;
}else{
return false;
}
try using below code
public function check_username_exists($username){
$query = $this->db->get_where('users', array('username' => $username));
if($query->num_rows() > 0){
return true;
}else{
return false;
}
}
It looks like you are trying to validate a form for user registration. If this is the case, then you would be far better off using the built-in form_validation library (https://codeigniter.com/user_guide/libraries/form_validation.html). There is already a built in method that would help you make sure you have unique records (such as the username). So basically, once the form_validation library is loaded, you would need to set the rules for validation. Once rules are set, you can call the run() method, which will produce a bool true/false whether it passes validation or not.
$this->load->library('form_validation');
$this->form_validation->set_rules('username', 'Username', "trim|required|is_unique[users.username]");
if($this->form_validation->run() === true) {
//Do something when posted form is validated
} else {
// There were errors or this is the first time
if(validation_errors())
$data['error_message'] = validation_errors();
}
You should use the form validation library to validate any (and usually all) of your forms throughout your application
Hope that helps
First Understand the Error: Can't use method return value in write context.
empty() needs to access the value by reference (in order to check whether that reference points to something that exists).
However, the real problem you have is that you use empty() at all, mistakenly believing that "empty" value is any different from "false".
Empty is just an alias for !isset($thing) || !$thing. When the thing you're checking always exists (in PHP results of function calls always exist), the empty() function is nothing but a negation operator.
Not Coming to your problem, As Scott Miller suggest you can use CodeIgniter validation. And if you want a little advance solution then you can put your validation in the config folder with creating form_validation.php for more detail visit CodeIgniter documentation for validation.
$config = array(
array(
'field' => 'username',
'label' => 'Username',
'rules' => 'required|is_unique[users.username]'
)
);
And If you want to do it with the query then here is the query:
public function check_username_exists($username){
$query = $this->db->get_where('users', array('username' => $username));
if ($query->num_rows() > 0){
return true;
}
else{
return false;
}
}
You can use num_rows to get the username exist or not.
How to test specific validation errors in php unit thrown in validation error ?
with below code we could check session has errors, but not the exact error
$this->assertSessionHasErrors();
assertSessionHasErrors can receive an array, as documented:
$this->assertSessionHasErrors([
'field' => 'Field error message.'
]);
Got the answer
$errors = session('errors');
$this->assertSessionHasErrors();
$this->assertEquals($errors->get('name')[0],"Your error message for validation");
$errors is MessageBag object which stored in laravel session when validation error thrown
using $errors->get('name') you could see all the validation errors as an array
You may use the combination of assertStatus and assertJson
...
->assertStatus(422)
->assertJson([
'errors' => [
'field' => [
'Error message'
]
]
]);
You can use $response->assertSessionHasErrors('key')
https://laravel.com/docs/7.x/http-tests#assert-session-has-errors
an example for required attribute will be
$response = $this->json('POST', '/api/courses', $this->data([
'name' => '',
'api_token' => $this->user->api_token
]));
$response->assertSessionHasErrors('name');
You can add an extra assertion, to make sure that no entry was added to the database, in this case "assert no course was added"
$this->assertCount(0, Course::all());
For multiple required attributes you may use a loop something like the following:
collect(['name', 'description', 'amount'])->each(function ($field) {
$response = $this->json('POST', '/api/courses', $this->data([
$field => '',
'api_token' => $this->user->api_token
]));
$response->assertSessionHasErrors($field);
$this->assertCount(0, Course::all());
});
First I use
$this->post()
instead of
$this->jsonPost()
Dont know why, for certain reason, the session would not come out.
Then I just use
$response->assertSessionHasErrors('field_name', 'Error Message!');
To find out what are the error message, you must dump it
$response->dumpSession();
There is also a more elegant way in my opinion:
If you throw an exception via the class GeneralException you can check in a unit test if the session has a flash_danger from throwing a exception.
Lets do a practical example: We want to test that the admin cannot activate an already activated catalogue item.
Test function
public function an_admin_cannot_activate_an_activated_catalogue()
{
$catalogue = factory(Catalogue::class)->states('active')->create();
$response = $this->get("/admin/questionnaire/catalogue/{$catalogue->id}/activate");
$response->assertSessionHas(['flash_danger' => __('The catalogue item is already activated.')]);
}
Model/Repro function
If it is activated we throw an Exception which then can be checked by the test function.
public function activate(Catalogue $catalogue) : Catalogue
{
if ($catalogue->is_active) {
throw new GeneralException(__('The catalogue item is already activated.'));
}
$catalogue->is_active = 1;
$activated = $catalogue->save();
if($activated) {
return $catalogue;
}
}
actually you can easily throw errors from validation using dd() and session('errors')
since errors bag is stored in session you could add dd(session('errors')) in your unit tests to see which fields you are missing.
and finally you can write more proper test by adding $response->assertSessionHasErrors('field_name');
Laravel 7;
In my case, I needed to ensure there was no error.
But below did ignore form-validation errors (at least mine).
$response->assertSessionHasNoErrors();
Hence I created a custom assert function in base TestCase class, like:
use PHPUnit\Framework\Constraint\RegularExpression;
// ...
public static function assertNoErrorReport(TestResponse $response)
{
$error = static::getViewError($response);
if ( ! empty($error)) {
$this->fail('View contains error:' . PHP_EOL . $error);
}
$response->assertSessionHasNoErrors();
}
public function assertHasErrorRegExp(string $pattern, TestResponse $response, string $message = '')
{
$error = static::getViewError($response);
static::assertThat($error, new RegularExpression($pattern),
empty($message) ? $error : $message);
}
public static function getViewError(TestResponse $response)
{
$content = $response->getOriginalContent();
if ( ! $content) {
static::fail('View content missing.');
}
if ($content instanceof View) {
$data = $content->gatherData();
$error = $data['error'] ?? $data['errors'] ?? null;
// Casts array to string.
if (is_array($error)) {
$error = '[' . join(', ', $error) . ']';
}
// Casts Error-bag to string.
$error = '' . $error;
if ($error === '[]') {
return null;
}
} else {
static::fail('Response is not a View.');
}
return $data;
}
However, my assertHasErrorRegExp(...) could be used for OP's case.
I have two field "password" (This field is in the database) and confirm_password (This field is not in the database)
Well, I need to compare if password == confirm_password.. but I'm not knowing create a custom validation to "confirm_password"... Would need to have this field in the database?
How do I do?
Generally you can access all data in a custom validation rule via the $context argument, where it's stored in the data key, ie $context['data']['confirm_password'], which you could then compare to the current fields value.
$validator->add('password', 'passwordsEqual', [
'rule' => function ($value, $context) {
return
isset($context['data']['confirm_password']) &&
$context['data']['confirm_password'] === $value;
}
]);
That being said, recently a compareWith validation rule was introduced which does exactly that.
https://github.com/cakephp/cakephp/pull/5813
$validator->add('password', [
'compare' => [
'rule' => ['compareWith', 'confirm_password']
]
]);
Now there has a method call sameAs in validator class, for version 3.2 or grater.
$validator -> sameAs('password_match','password','Passwords not equal.');
see API
I know it's late answer but will help to other.
// Your password hash value (get from database )
$hash = '$2y$10$MC84b2abTpj3TgHbpcTh2OYW5sb2j7YHg.Rj/DWiUBKYRJ5./NaRi';
$plain_text = '123456'; // get from form and do not make hash. just use what user entred.
if (password_verify($plain_text, $hash)) {
echo 'Password is valid!';
} else {
echo 'Invalid password.';
}
OR
$hasher = new DefaultPasswordHasher();
$check = $hasher->check($plain_text,$hash); // it will return true/false