I am stuck with this problem. When the login credentials are authenticated in my zend application , I also want to check if the account has been confirmed or not. Confirmed is a boolean column in my account table and is set to False by default. I am trying to achieve this through following code..but it is not working
$db = Zend_Db_Table::getDefaultAdapter();
$authAdapter = new Zend_Auth_Adapter_DbTable($db);
$authAdapter->setTableName('Account');
$authAdapter->setIdentityColumn('Email');
$authAdapter->setCredentialColumn('Password');
$authAdapter->setCredentialTreatment('Confirmed = 1');
$authAdapter->setIdentity($data['email']);
$authAdapter->setCredential($data['password']);
$auth = Zend_Auth::getInstance();
$result = $auth->authenticate($authAdapter);
if ($result->isValid()) {
if ($data['public'] == "1") {
Zend_Session::rememberMe(Zend_Registry::getInstance()->constants->sessiontime);
} else {
Zend_Session::forgetMe();
}
return TRUE;
} else {
return FALSE;
}
Despite the account not confirmed the authentication passes. Please tell me where am I wrong
The credential treatment parameter specifies how the password should be checked. You can override this to add additional clauses, but you still need to include the password bit. Really I wouldn't have expected your method to authenticate any users, so this may not be the main issue, but try:
$authAdapter->setCredentialTreatment('MD5(?) AND Confirmed = 1');
Changing the MD5 bit for however your passwords are encrypted. That should generate a query along the lines of:
... WHERE Email = 'xxx' AND Password = MD5(?) AND Confirmed = 1
Related
I am get login errors when i test my script by logining under my own account. Do you think hashing passwords twice a bad practice?
I have hashed the users password twice in my website. Once, when they register and once, when they update their password in account update. Also i am using bcrypt method and cost of bcrypting is 10 on both hashings and i am on localhost server.
///// this is the code in register.php page
<?php
if(isset($_POST['registeruser'])) {
session_start();
$FName = $_POST['regfname'];
$LName = $_POST['reglname'];
$Email = mysqli_real_escape_string($conn, $_POST['regemail']);
$origignalpassword = preg_replace('#[^a-z0-9_]#i', '',
$_POST['regpassword']);
$Passwordw = $_POST['confirmedpassword'];
$infosql = "SELECT * FROM users WHERE useremail = '".$Email."'";
$result = mysqli_query($conn,$infosql);
if(mysqli_num_rows($result)>=1)
{
echo "Email already taken.";
}
else if(mysqli_num_rows($result) !=1 && $Passwordw ==
$origignalpassword) {
$Passwordhash = password_hash($Passwordw,
PASSWORD_BCRYPT, array('cost' => 10));
$sql = $conn->query("INSERT INTO users(firstname,
lastname, useremail, Passwordcell) Values('{$FName}',
'{$LName}','{$Email}','{$Psswordhash}')");
header('Location: login.php');
} else {
echo 'Please check your password:' . '<br>';
}
}
?>
//// Below code is the code in my update.php page
<?php session_start();
if(isset($_SESSION['user_id'])) {
} else {
header('Location: login.php');
}
$user = $_SESSION['userid'];
$myquery = "SELECT * FROM users WHERE `userid`='$user'";
$result = mysqli_query($conn, $myquery);
$row = mysqli_fetch_array($result, MYSQLI_BOTH);
$_SESSION['upd_fnames'] = $row['firstname'];
$_SESSION['upd_lnames'] = $row['Lastname'];
$_SESSION['upd_emails'] = $row['useremail'];
$_SESSION['upd_passwords'] = $row['Passwordcell'];
$_SESSION['upd_phone'] = $row['phonenum'];
$_SESSION['upd_bio'] = $row['biography'];
?>
<?php
if (isset($_POST['updateme'])) {
$updfname = $_POST['upd_fnames'];
$updlname = $_POST['upd_lnames'];
$updemail = $_POST['upd_emails'];
$updphone = $_POST['upd_phone'];
$upd_pswd = $_POST['upd_passwords'];
$biography = $_POST['update_biography'];
$Pswod = password_hash($upd_pswd, PASSWORD_BCRYPT,
array('cost' => 10));
$sql_input = $mysqli->query("UPDATE users SET firstname = '{$updfname}', Lastname = '{$updlname}', Phonenum = '{$updphone}', useremail = '{$updemail}', Passwordcell = '{$Pswod}', biography = '{$biography}' WHERE userid=$user");
header('Location: Account.php');
}
else
{
}
?>
Your problem could be just a typo, in your registration script, instead of $Passwordhash you wrote:
"INSERT INTO users(..., Passwordcell) Values(...,'{$Psswordhash}')"
Nevertheless there are other problems with your code, and since you asked for advise, i would like to share my thoughts.
Probably the biggest problem is, that your code is vulnerable to SQL-injection. Switch to prepared statements as soon as you can, writing code will become even easier than building the query as you did, and both MYSQLI and PDO are supporting it. This answer could give you a start.
Passwords should not be sanitized. Remove the line $origignalpassword = preg_replace('#[^a-z0-9_]#i', '', $_POST['regpassword']), and just pass the input directly to the hash function password_hash($_POST['regpassword'], PASSWORD_DEFAULT). The password_hash() function works with any type of input.
It is a good habit to place an exit after each redirection, otherwise the script will continue executing. header('Location: login.php', true, 303); exit;
Do you really have reason to put the user info into the session? Instead of $_SESSION['upd_fnames'] = $row['firstname']; i would fetch the information on demand from the database. With fetching it from the database you can be sure that the information is actually set (is not null) and is up to date, you can avoid a state and you get a bit more REST full.
Then last but not least i would recommend to follow some style rules, like starting variable names always with a small letter. You can avoid some silly typos and it makes your code more readable.
I have a scenario where i need to check some additional columns while doing the authentication. This is because, the application stores some usernames in database and some in LDAP. the authentication precedence is for usernames in database. If username exist in database, we will not check in LDAP else we will check it in LDAP.
For LDAP users, we are keeping a copy of there usernames in same "user" table with a blank password column. To disgusting both group of users, there is an additional column called userDirectory with values "LDAP and INTERNAL". we have to keep a copy of LDAP usernames for application specific settings and all.
Also username+userDirectory is a uniqueKey
Now my problem is, sometimes there will be multiple users with same username but in different userDirectory. as mentioned above LDAP users will not have a password stored in database and that authentication is a separate code snippet.
I am using the below code for DB authentication. Even though i am adding a condition setCredentialTreatment('md5(?) AND userDirectory="internal"'), it is searching LDAP users also. HOW do i restrict this for userDirectory='internal'
$dbAdapter = Zend_Db_Table::getDefaultAdapter();
$authAdapter = new Zend_Auth_Adapter_DbTable($dbAdapter);
$authAdapter->setTableName('users')
->setIdentityColumn('username')
->setCredentialColumn('password')
->setCredentialTreatment('md5(?) AND userDirectory="internal"');
$authAdapter->setIdentity($username);
$authAdapter->setCredential($password);
I have changed your code:
$dbAdapter = Zend_Db_Table::getDefaultAdapter();
$authAdapter = new Zend_Auth_Adapter_DbTable($dbAdapter);
$authAdapter->setTableName('users')
->setIdentityColumn('username')
->setCredentialColumn('password')
->setCredentialTreatment('MD5(?)'); // changed
$authAdapter->setIdentity($username);
$authAdapter->setCredential($password);
$authAdapter->getDbSelect()->where('userDirectory = "internal"'); // added
http://framework.zend.com/manual/1.12/en/zend.auth.adapter.dbtable.html
check the last code under Advanced Usage By Example, code is as follows
$registry = Zend_Registry::getInstance();
$DB = $registry['DB'];
$authAdapter = new Zend_Auth_Adapter_DbTable($DB,'usertable','username','password');
$authAdapter->setIdentity($request->getParam('username'));
$authAdapter->setCredential($request->getParam('password'));
$select = $authAdapter->getDbSelect();
$select->where('`active` = 1');
$result = $authAdapter->authenticate();
if($result->isValid()){
//set user proper to zend instance
$this->_redirect('/index');
}
else
{
//logout or redirect to same page
}
Extends Zend_Auth_Adapter_DbTable and override _authenticateCreateSelect() method like this
protected function _authenticateCreateSelect()
{
$select = parent::_authenticateCreateSelect();
return $select->where('userDirectory = ?','internal');
}
I want to check the user table for status=1 as extra validation. My getAuthAdapter() method looks like this:
private function getAuthAdapter (){
$authAdapter = new Zend_Auth_Adapter_DbTable(Zend_Db_Table::getDefaultAdapter());
$authAdapter->setTableName('vi_users')
->setIdentityColumn('email')
->setCredentialColumn('password');
return $authAdapter;
}
How would I alter this to include the extra validation?
I ran across this problem myself, and I'm not sure it is possible to check another column at the sametime. So I do it straight aftwerwards, with something like this:
$auth = Zend_Auth::getInstance();
....
if (process($form->getValues())) {
// login credentials are correct, so we now need to check if their account is activated
if ($auth->getIdentity()->active != 1) {
// if not, log them out and tell them to activate
Zend_Auth::getInstance()->clearIdentity();
$output .= "Your account has not yet been activated, please check your email (including spam bin) for the activation link.";
} else {
// if they're active then login is successful
$output .= "You are now logged in";
}
} else {
// username/password wrong
$output .= "Credentials invalid";
}
Update: Given Orlando's answer, it looks like what you've asked for originally is possible. You could use my solution though if you would like to distinguish between status value being 'wrong' and user/pass being wrong.
You could do something like:
$adapter = new Zend_Auth_Adapter_DbTable(
$db,
'users',
'username',
'password',
'MD5(?) AND status = 1'
);
where $db would be Zend_Db_Table::getDefaultAdapter()
Taken from http://framework.zend.com/manual/en/zend.auth.adapter.dbtable.html
I'm having a difficult time understanding how Zend_Session_Namespace is integrated with Zend_Auth. I have this method I'm using as the action to Authenticate my login page-it is working correctly and redirecting to the /monthly view:
public function authAction(){
$request = $this->getRequest();
$registry = Zend_Registry::getInstance();
$auth = Zend_Auth::getInstance();
$DB = $registry['DB'];
$authAdapter = new Zend_Auth_Adapter_DbTable($DB);
$authAdapter->setTableName('users')
->setIdentityColumn('UserName')
->setCredentialColumn('Password');
// Set the input credential values
$uname = $request->getParam('UserName');
$paswd = $request->getParam('Password');
$authAdapter->setIdentity($uname);
$authAdapter->setCredential($paswd);
// Perform the authentication query, saving the result
$result = $auth->authenticate($authAdapter);
// TRYING TO SET THE NAMESPACE
$this->session = new Zend_Session_Namspace('UserName');
if($result->isValid()){
$data = $authAdapter->getResultRowObject(null,'password');
$auth->getStorage()->write($data);
$this->_redirect('/monthly');
}else{
$this->_redirect('/login');
}
}
But I need to be able to store UserName as a Zend_session and call it from monthly controller. I'm not doing things right because I just get a blank screen when I try and do this:
public function indexAction()
{
$this->view->userName = Zend_Session_Namespace('UserName');
}
With the lines:
$data = $authAdapter->getResultRowObject(null,'password');
$auth->getStorage()->write($data);
You're writing all the user's information, except the password, which is OK. Where ever you need to access the logged in user's details, you can do something like (updated as per your comment):
public function indexAction() {
$auth = Zend_Auth::getInstance();
if($auth->hasIdentity()) {
$userData = $auth->getIdentity();
$this->view->user = $userData;
}
}
in the view file (index.phtml) just: echo $this->user->firstname
That's it. If one day you decide to change the storage for Zend_Auth from session, to, for example, database, this piece of code will still work.
Youre not useing the correct namespace. Zend_Auth use the Zend_Auth namespace. The namespace is the structure, not the key for a value. so your session looks something like this:
Array('Zend_Auth' => array ('UserName' => 'myname')
Well thats not accurate exactly because you havent stored the user name unless youve provided for this directly in your adapter. youll need to do something like:
$auth->getStorage()->UserName = 'myusername';
Then you can access with $authData = new Zend_Session_Namespace('Zend_Auth'); $username = $authData->UserName;.
Take a closer look at how the Zend_Auth_Adapter_Db works.
This is my approach and it s working nice:
1-i start by defining an init function in the bootstrap
protected function _initSession()
{
$UserSession = new Zend_Session_Namespace('UserSession');
$UserSession->setExpirationSeconds(/* you may fix a limit */);
Zend_Registry::set('UserSession', $UserSession);
}
/* in the Login action,after correct username & pwd */
// Create session
$UserSession = Zend_Registry::get('UserSession');
// Get the user from database or just from fields
//you have to make sure that the psswd is encrypted use MD5 for example..
$db = Zend_Db_Table::getDefaultAdapter();
$user = $db->fetchRow("SELECT * FROM user_table WHERE user_email = '".$user_email."'");
//then you assign to $user to $UserSession variable :
$UserSession->user = $user;
//finaly don't forget to unset session variable in the Logout action ...
So I am using Zend_Auth to authenticate users of my website. Currently they are only able to log in with their email as login but I would like to enable them to log in also with their username.
Here is some code:
// prepare adapter for Zend_Auth
$adapter = new Zend_Auth_Adapter_DbTable($this->_getDb());
$adapter->setTableName('users');
$adapter->setIdentityColumn('email');
$adapter->setCredentialColumn('password_hash');
$adapter->setCredentialTreatment('CONCAT(SUBSTRING(password_hash, 1, 40), SHA1(CONCAT(SUBSTRING(password_hash, 1, 40), ?)))');
$adapter->setIdentity($request->getParam('email'));
$adapter->setCredential($request->getParam('password'));
Notice the line:
$adapter->setIdentityColumn('email');
How can I add also username there (column in the database called username, too)?
UPDATE:
This is how I solved this:
$login = $request->getParam('email');
$validator = new Zend_Validate_EmailAddress();
if (false === $validator->isValid($login)) {
$u = $this->_getTable('Users')->getSingleWithUsername($login);
if (null === $u) {
throw new Exception ('Invalid login and/or password');
}
$login = $u->email;
}
// prepare adapter for Zend_Auth
$adapter = new Zend_Auth_Adapter_DbTable($this->_getDb());
$adapter->setTableName('users');
$adapter->setIdentityColumn('email');
$adapter->setCredentialColumn('password_hash');
$adapter->setCredentialTreatment('CONCAT(SUBSTRING(password_hash, 1, 40), SHA1(CONCAT(SUBSTRING(password_hash, 1, 40), ?)))');
$adapter->setIdentity($login);
$adapter->setCredential($request->getParam('password'));
I deal with the same thing, and I handle it before Zend_Auth. I use a single user sign-in field and first check whether it's an email address -- if so, it's converted to the appropriate username. Then, let Zend_Auth do its thing.
This works well for me, although you'll need to kinda switch it around, since you're going the other way.
i. Add a filter to your user sign-in field, like this:
$user_field->addFilter('EmailToUsername');
ii. The filter:
<?php
/**
* Converts an email address to the username.
*/
class Prontiso_Filter_EmailToUsername implements Zend_Filter_Interface
{
public function filter( $value )
{
if ( Zend_Validate::is($value, 'EmailAddress') ) {
$user_table = new Users();
$user = $user_table->findByEmail($value);
if ( $user ) {
return $user->username;
}
}
/**
* Nothing happened, so don't filter.
*/
return $value;
}
}
As for just changing a Zend_Auth setting instead, Zend_Auth doesn't like either/or identity columns, so you'd have to write your own auth adapter.
My own solution:
$adapter->setIdentityColumn(stripos($indentity, '#') ? 'email' : 'username');
Fast and simple!