I'm working on an assignment on a PHP course, and I'm stucked at the last part of it.
The assignment is to create a simple login form and use a session as well as hardcoded usernames and passwords (i.e. no db).
What I have problems with is the class that handles the login, and sessions especially. There's a lot of code and I didn't know what I could remove and therefore I've put it on Pastebin instead, hope that's alright.
Thing is that the unit tests that's built into the class passes except for nr. 4, the one that's checking that the user is logged in. The problem seems to be that $_SESSION[$this->loginSession] doesn't get set, and this is what I need help with.
The variable $loginSession is declared in the beginning of the class, and should be set to "isLoggedIn" when a user types a correct username and password, but that doesn't happen (no error message).
My class is:
<?php
class LoginHandler {
private $loginSession;
public function IsLoggedIn() {
if($_SESSION[$this->loginSession] == "isLoggedIn") {
return true;
}
else {
return false;
}
}
public function DoLogin($username, $password){
if ($username != null && $password != null){
switch ($username){
case "hello";
if ($password == "1234"){
$_SESSION[$this->loginSession] == "isLoggedIn";
return true;
}
else return false;
case "hello2";
if ($password == "12345"){
$_SESSION[$this->loginSession] == "isLoggedIn";
return true;
}
else return false;
}
}
else {
return false;
}
}
public function DoLogout(){
unset($_SESSION[$this->loginSession]);
}
public function Test() {
$this->DoLogout();
// Test 1: Check so you're not logged in.
if($this->IsLoggedIn() == true){
echo "Test 1 failed";
return false;
}
// Test 2: Check so that it's not possible to login with wrong password.
if ($this->DoLogin("hello", "4321") == true){
echo "Test 2 failed";
return false;
}
// Test 3: Check that it's possible to log in.
if ($this->DoLogin("hello", "1234") == false){
echo "Test 3 failed";
return false;
}
// Test 4: Check that you're logged in
if ($this->IsLoggedIn() == false){
echo "Test 4 failed";
return false;
}
return true;
}
}
?>
I hope it's enough to include the class and not all the other files, otherwise I'll put them up.
Now I see it :-)
$_SESSION[$this->loginSession] == "isLoggedIn";
== should be =
== compares while = sets
You need to start the session. session_start(); Place it at the very top of the documents (only one time on a page load) you are using.
$this->loginSession is never set so it's NULL
$_SESSION[null] is not possible as far as i know
change your code to
private $loginSession = 'testing';
and it should work
Why do you put semicolon in your case instruction case "hello";There should be a colon. case "hello": { ...instructions}
Related
I am developing a Register/Login system with validation. Registering system is working well. For example, when I register the same email twice, the following message appears:
Email already registered!
However, when I log-in with the same e-mail and password, an error occurs. The following message appears as a validation error:
Email not registered!
Even if the email is registered in DB.
Code for e-mail validation:
<?php
public function validateEmail($par)
{
if (filter_var($par, FILTER_VALIDATE_EMAIL)) {
return true;
} else {
$this->setErro("Invalid Email!");
return false;
}
}
public function validateIssetEmail($email, $action = null)
{
$b = $this->cadastro->getIssetEmail($email);
if ($action == null) {
if ($b > 0) {
$this->setErro("Email already registered!");
return false;
} else {
return true;
}
} else {
if ($b > 0) {
return true;
} else {
$this->setErro("Email not registered!");
return false;
}
}
}
Code for login controller:
<?php
$validate = new Classes\ClassValidate();
$validate->validateFields($_POST);
$validate->validateEmail($email);
$validate->validateIssetEmail($email,"login");
$validate->validateStrongSenha($senha);
$validate->validateSenha($email,$senha);
var_dump($validate->getErro());
Code for class login:
<?php
namespace Models;
class ClassLogin extends ClassCrud
{
# Returns user data
public function getDataUser($email)
{
$b = $this->selectDB(
"*",
"users",
"where email=?",
array(
$email
)
);
$f = $b->fetch(\PDO::FETCH_ASSOC);
$r = $b->rowCount();
return $arrData = [
"data" => $f,
"rows" => $r
];
}
}
My getIssetEmail method exists on Register code only.
# Check directly at the bank if the email is registered
public function getIssetEmail($email)
{
$b = $this->selectDB(
"*",
"users",
"where email=?",
[
$email
]
);
return $r = $b->rowCount(); // returns the amount of rows in the search
}
And ClassPassword
<?php
namespace Classes;
use Models\ClassLogin;
class ClassPassword
{
private $db;
public function __construct()
{
$this->db = new ClassLogin();
}
# Create password's hash to save in DB
public function passwordHash($senha)
{
return password_hash($senha, PASSWORD_DEFAULT);
}
# Verify if password's hash is correct
public function verifyHash($email, $senha)
{
$hashDb = $this->db->getDataUser($email);
return password_verify($senha, $hashDb["data"]["senha"]);
}
}
This is not an answer but hopefully it will help in debugging.
First, I'm going to change your code. This is 100% a style choice but I personally think it is easier to follow. If you have an if statement that always returns, you don't technically need an else. Once again, this is a style choice and you don't have to follow it.
Second, if you can, try adding logging into your workflow, it will save you so much time debugging. It isn't always an option, especially for legacy code bases, but it is awesome when you can inspect complex code. In this example, I"m just making a couple of helper methods that dump stuff but normally I'd use something like Monolog to write to a stream that I can tail, and I can easily turn it off in production. When logging, sometimes it helps to avoid identical messages so that you can easily find the exact line number you are on, too.
So with those changes, try running this code inside of your class:
private function logMessage($message)
{
echo $message . PHP_EOL;
}
private function logVariable($variable)
{
var_dump($variable);
}
public function validateIssetEmail($email, $action = null)
{
$this->logVariable($email);
$this->logVariable($action);
$b = $this->cadastro->getIssetEmail($email);
$this->logVariable($b);
if ($action === null) {
$this->logMessage('Action was null');
if ($b > 0) {
$this->logMessage('B is greater than zero');
$this->setErro("Email already registered!");
return false;
}
$this->logMessage('B was not greater than zero');
return true;
}
$this->logMessage('Action was not null');
if ($b > 0) {
$this->logMessage('B is greater than zero');
return true;
}
$this->logMessage('B was not greater than zero');
$this->setErro("Email not registered!");
return false;
}
This should log in human-readable form every step. You should be able to walk through this and identify where your bug is. For instance, in the comments above you said that a variable was 0 in a block that was guarded by a check that guarantees that that shouldn't happen.
This is the wrong part i guess you assigned login as action so you can call cadastro class inside of the function
$cadastro = new Cadastro();
$b = $cadastro->getIssetEmail($email);
if ($action == null) {
if ($b > 0) {
$this->setErro("Email already registered!");
return false;
} else {
return true;
}
} else {
if ($b > 0) {
return true;
} else {
$this->setErro("Email not registered!");
return false;
}
}
I hope you are doing great. I have a class in my project with various methods. The thing that happens is, one of the methods is supposed to return a Boolean variable. Instead. It returned the number "1" instead of false. If you could tell me where is the problem.
Thanks in advance, Cheers.
Useful pieces of my code:
Class method:
public function validatePwd($pwd1, $pwd2) {
if (strcasecmp($pwd1, $pwd2) == 0)
{
return true;
}
else
{
return false;
}
}
The script that executes it:
$check1 = $user->validatePwd($Password, $Password1);
echo $check1;
if ($user->validatePwd($Password, $Password1))
{
}
else
{
$errors[] = 'Error!, passwords entered are not compatible, Please'
. ' enter passwords that match each other';
}
If you do var_dump($check1) instead of echo $check1; it should show as boolean.
I have two seperate if statements, the first if statement is not working but the second one is.
The first if statement works on my other pages and I am unsure of how to properly code this as I am a beginner to PHP.
<?php
session_start();
if($_SESSION['loggedin'] != 'true') {
header("location:login.php");
}
if ($_SESSION['admin']=='N') {
header("location:errorpage.php");
}
?>
What is true in your conditions? It can be bool type or string type.
If You set like this:
$_SESSION['loggedin'] = TRUE;
$_SESSION['loggedin'] = 'true';
You have got two different variable sets.
You can compare it using == or === to include variable type.
For example:
$_SESSION['test_1'] = TRUE;
$_SESSION['test_2'] = 'true';
var_dump( $_SESSION );
array(2) { ["test_1"]=> bool(true) ["test_2"]=> string(4) "true" }
$_SESSION['loggedin']?
Why don't just clear every SESSION var on logout and if the SESSION vars are set => the user is logged in.
And use after the header(); an exit();
Try var_dump($_SESSION['loggedin']) and edit your question.
Or maybe your loggedin var is not a string but a boolean so you could do if(!$_SESSION['loggedin'])
Try using Boolean values rather than strings. I would also use a const for the admin variables. I would do the following;
$_SESSION['loggedin'] = true/false;
$_SESSION['admin'] = true/false;
public class Priviledges
{
public CONST Admin = 0;
public CONST User = 1;
public CONST Contributor = 3;
//change this to however you want to do it :)
public static function isAdmin($val)
{
if ($val == Priviledges::Admin)
{
return true;
}
else
{
return false;
}
}
}
then when you set the admin session variable you can go;
$_SESSION['admin'] = Priviledges::Admin;
if(!$_SESSION['loggedin'])
{
header("location:login.php");
exit()
}
else if (!Priviledges::isAdmin($_SESSION['admin']))
{
header("location:errorpage.php");
exit()
}
else
{ //do your stuff if none of these conditions are met.. }
Always add an exit() or die() after sending a "Location" HTTP header:
<?php
session_start();
if($_SESSION['loggedin'] !== 'true') {
header("location:login.php");
exit();
}
if ($_SESSION['admin'] === 'N') {
header("location:errorpage.php");
exit();
}
Check: php - Should I call exit() after calling Location: header?.
From aaronsaray blog:
Remember, just because the browser is smart enough not to show the
content, doesn’t mean that this isn’t dangerous. So, it’s a little
less dangerous say if this page is just showing a user search option
or some information. It is much more dangerous if this is a page that
executes an action. This is because the entire PHP page will execute
if you don’t put a die() statement.
On other cases, if you want a condition to be evaluated only when a previous condition is false, you may use a "else if".
I've noticed if the session is dead (on an Jquery AJAX PHP Request) the data returned is preceeded with an error message if the session is needed in the request.
How do other sites deal with this?
Similar code is shown below - eg: its using a SESSION variable in the code which doesn't exist as the session is dead.
public function internal($variable) {
if($_SESSION['data'] == $variable) {
echo TRUE;
}else{
echo FALSE;
}
}
Should I use isset to check if the variable exists?
Should I be coding for dead sessions?
thx
You need to add the isset also:
public function internal($variable) {
if(isset($_SESSION['data']) && $_SESSION['data'] == $variable) { //add here
echo TRUE;
}else{
echo FALSE;
}
}
Try this
public function internal($variable) {
if($_SESSION['data']!="" && $_SESSION['data'] == $variable) { //add here
echo TRUE;
}else{
echo FALSE;
}
}
You can also do like this,
public function internal($variable) {
if(!empty($_SESSION['data']) && $_SESSION['data'] == $variable) { // modify here
echo TRUE;
}else{
echo FALSE;
}
}
I've got a "make-do" page authenticator which defines what usergroups are allowed to access that page, however, some of my scripts allow the user to pass if that page is, say, his user edit page but not touch any other users edit page. For that, I disabled access to the usergroups except if you're an admin or the user edit page you are currently on is your own.
I tried to create a function to do this, but the allowOnly usergroups function deals out the punishment without checking to see if the other function is defined elsewhere on the page.
Here's the "make-do" functions and an example of how I'd like them to work:
public function allowOnly($officer, $administrator, $superuser)
{
$authority = 0;
if ($officer == true && $this->session->isOfficer()) {
$authority++;
}
elseif ($administrator == true & $this->session->isAdmin()) {
$authority++;
}
elseif ($superuser == true & $this->session->isSuperuser()) {
$authority++;
}
if ($authority != 0) {
return true;
}
else {
header("Location: ../incorrectRights.php");
exit;
}
}
function allowCurrentUser()
{
global $authority;
$authority++;
}
This changes the users location if they're not any of the allowed usergroups, but since that code is executed before "allowCurrentUser", it changes the location before the function gets the chance to allow the user through.
I'd like it to work like this:
<?php
include("functions.php");
$functions->allowOnly(false, false, true);
if($session->username == $allowedUserName) {
$functions->allowCurrentUser();
}
I'm sorry if I'm not descriptive enough, or my code lacks efficiency, heck, even if I've missed a built-in php function which does this for me!
you should check out PHP's function_exists(), this will tell you wether or not the function already exist.
you have some error in your code too.
$administrator == true & $this->session->isAdmin()
should be
$administrator == true && $this->session->isAdmin()
as you have used only single & whereas it should be &&
and also change
$superuser == true & $this->session->isSuperuser()
to
$superuser == true && $this->session->isSuperuser()
after reading your code, i realized you are using $authority variable to hold the value and to check wether to authorize user or not. and plus you are using global. i would never have done that way, instead i would declare $authority as class property below is the example of how you could do it.
class functions
{
//declare class propert and set default value to 0
protected $_authority = 0;
public function allowOnly($officer, $administrator, $superuser)
{
if ($officer == true && $this->session->isOfficer()) {
$this->_authority++;
}
elseif ($administrator == true && $this->session->isAdmin()) {
$this->_authority++;
}
elseif ($superuser == true && $this->session->isSuperuser()) {
$this->_authority++;
}
if ($this->_authority != 0) {
return true;
}
else {
header("Location: ../incorrectRights.php");
exit;
}
}
public function allowCurrentUser()
{
$this->_authority++;
return $this->_authority;
}
}
UPDATE:
instead of redirecting the page why not return false and redirect during function call, you can do it this way.
class functions
{
//declare class propert and set default value to 0
protected $_authority = 0;
public function allowOnly($officer, $administrator, $superuser)
{
if ($officer == true && $this->session->isOfficer()) {
$this->_authority++;
}
elseif ($administrator == true && $this->session->isAdmin()) {
$this->_authority++;
}
elseif ($superuser == true && $this->session->isSuperuser()) {
$this->_authority++;
}
return ($this->_authority != 0) ? true : false;
}
public function allowCurrentUser()
{
$this->_authority++;
return $this->_authority;
}
}
and while in function call.
include("functions.php");
if($functions->allowOnly(false, false, true)) {
//person is allowed access
}
//else allow current user
elseif($session->username == $allowedUserName) {
$functions->allowCurrentUser();
}
else {
//redirect here
header("Location: ../incorrectRights.php");
exit;
}
I'm not completely certain if this is the answer you are looking for based on the title but this is what I get the impression you are asking for.
Assuming what happens is that your check against allowOnly() takes the user to the "../incorrectRights.php"-page before you've checked if the user logged in is the same as the page looked at, what you need to do is put the check inside the allowOnly function or at least before you do the check for $authority != 0.
Here's a quick example of how you could solve this:
public function allowOnly($officer, $administrator, $superuser)
{
$authority = 0;
if ($officer == true && $this->session->isOfficer()) {
$authority++;
}
elseif ($administrator == true && $this->session->isAdmin()) {
$authority++;
}
elseif ($superuser == true && $this->session->isSuperuser()) {
$authority++;
}
if(function_exists('allowCurrentUser')){
if (allowCurrentUser()) {
return true;
}
}
if ($authority != 0) {
return true;
}
else {
header("Location: ../incorrectRights.php");
exit;
}
}
function allowCurrentUser()
{
if($session->username == $allowedUserName){
return true;
}
else {
return false;
}
}
Then your useage would result in something more like
<?php
include("functions.php");
$functions->allowOnly(false, false, true);
?>
As you can see I also threw in the function_exists('functionnamehere') call that seems to be requested in the question title, since we actually declare the function and therefore know it exists you could also just do this:
if ($authority != 0 || allowCurrentUser()) {
return true;
}