Laravel 4 failing Auth::attempt with Hash replacement - authentication

I'm trying to do authentication in a Laravel4 project, but it always fails.
Since I'm only on PHP 5.3.4 I downloaded a replacement Hash package that uses SHA512 from https://github.com/robclancy/laravel4-hashing.
I already have a Users table (the Password field is pre-populated with SHA512 hashes, shared with another app) and my login auth method is:
Route::post('login', function()
{
$userdata = array(
'UserName' => 'SteB',
'password' => '1234'
);
if (Auth::attempt($userdata, true))
{
return Redirect::action('HomeController#Profile');
}
else
{
return Redirect::to('/')->with('login_errors', true);
}
});
I've also checked the SHA512 hash is correct using http://hash.online-convert.com/sha512-generator
This always fails (silently), any ideas why or how to debug it will be appreciated.
My Users table is:
UserID int, Identity PK
UserName varchar(24)
FullName varchar(32)
EMail varchar(64)
Password varchar(256)
This is in an existing SQL Server 2000 database, I'm getting User info out of the DB for an un-authenticated page, so I know the DB driver and connection is working ok.
User Model in Laravel:
use Illuminate\Auth\UserInterface;
use Illuminate\Auth\Reminders\RemindableInterface;
class User extends Eloquent implements UserInterface, RemindableInterface
{
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'Users';
/**
* The attributes excluded from the model's JSON form.
*
* #var array
*/
protected $hidden = array('password');
//protected $fillable = array('UserID', 'UserName', 'FullName');
/**
* Get the unique identifier for the user.
*
* #return mixed
*/
public function getAuthIdentifier()
{
return $this->getKey();
}
/**
* Get the password for the user.
*
* #return string
*/
public function getAuthPassword()
{
/*Now all lowercase to match $userdata as suggested on SO*/
return $this->password;
}
/**
* Get the e-mail address where password reminders are sent.
*
* #return string
*/
public function getReminderEmail()
{
return $this->EMail;
}
}
UPDATE:
Just found this article: http://laravel.io/topic/20/hashing-in-laravel.
Looks like it's not doing things how I expected, a base64-encoded hash with random salt, I think.

So you need to change something in your model as well as the key you pass to attempt
class User extends Eloquent implements UserInterface
{
public function getAuthPassword()
{
return $this->Password;
}
// leave other code the same
}
And in your array you pass change it like follows.
$userdata = array( 'UserName' => 'SteB', 'password' => '1234');
The getAuthPassword() tells the Auth library your actual fieldname and setting the key in the userdata to all lowercase password tells the Auth library to hash that field.

I have no idea what your database structure looks like, but could it be that your username key isn't the same are you database column?
Maybe this will work:
$userdata = array(
'username' => 'SteB',
'password' => '1234'
);
Cheers.

EDIT: you can try all this etc, but I think I just proved myself wrong right after writing this.
This issue is to do with your database column naming and a restriction in Laravel I believe. Looking into Laravel's auth component I find a few areas where it specifically wants password or at least the string to contain that exact phrase. So your database needs to use the password column or you need to extend laravel to change some things.
Honestly, you should only ever use camel case or snake case with laravel. Issues can come up like this one when you use something it will never expect because there is no logical reason to use anything else.
So easy fix, use better column name.
Harder fix is extending the Auth component to allow for your Password field.

Related

Laravel 8 - Class 'Database/Factories/User' not found

I have this PostFactory.php file in database->factories directory:
<?php
namespace Database\Factories;
use App\Models\Post;
use Illuminate\Database\Eloquent\Factories\Factory;
class PostFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* #var string
*/
protected $model = Post::class;
/**
* Define the model's default state.
*
* #return array
*/
public function definition()
{
return [
'user_id' => User::factory(),
'title' => $this->faker->sentence,
'message' => $this->faker->paragraph
];
}
}
Now, when I run this command
Post::factory()->create();
from the tinker
I got that error message
Class 'Database/Factories/User' not found
:( Is there anything I am missing?
You need to import the User Model.
For Laravel 8, Your PostFactory.php file should look like so;
<?php
namespace Database\Factories;
use App\Models\User;
use App\Models\Post;
use Illuminate\Database\Eloquent\Factories\Factory;
class PostFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* #var string
*/
protected $model = Post::class;
/**
* Define the model's default state.
*
* #return array
*/
public function definition()
{
return [
'user_id' => User::factory(),
'title' => $this->faker->sentence,
'message' => $this->faker->paragraph
];
}
}
check laravel docs on writing factories for more info.
UPDATE:
As for the error here on prnt (picked it up in the comments), You will need to provide more information.
However to start you up consider checking your database for:
A post that does not have a user_id. I.e one that you might have added before adding the foreign key constraint and therefore does not belong to any user.
If that's the case consider removing it or use tinker to manually assign a foreign key(i.e associate the post with a user) then try and create factories again. As you are trying to enforce a required column to existing data that does not already have it.
FYI just run:
composer dump-autoload
It can be that the class is not autoloaded.
As i Also had same Issue .First i changed my factory name same as name of model . Like if we have Blog Model .. We will make BlogFactory . so it can find the name of factory .

Custom Symfony FOSUserBundle service for migrating legacy passwords

I am trying to plug in my own legacy password service into Symfony3 to passively migrate users from a legacy database table.
The legacy system has passwords hashe with the same hard-coded $salt variables used across all members (therefore my FOSUserBundle table currently has the salt column empty for all members that are to be migrated).
The legacy method uses:
sha1($salt1.$password.$salt2)
The new method is Symfony's FOSUserBundle standard bcrypt hash.
I am trying to implement it so that when a legacy user first logs in, Symfony will try to:
Log in using FOSUserBundle's standard bcrypt method.
If #1 did not succeed then try the legacy algorithm.
If #2 succeeeds the password hash and salt in the database table will be updated to comply with standard FOSUserBundle method
I have been reading around about how to plug in a service to get this working and I think the below that I have seems to be correct in theory - if not any corrections/guidance would be appreciated as I've not been able to test it!
However, I'm unsure how I should go about connecting it all into Symfony so that the normal FOSUserBundle processes will carry out steps 2 and 3 if step 1 fails
services.yml:
parameters:
custom-password-encoder:
class: AppBundle\Security\LegacyPasswordEncoder
security.yml:
security:
encoders:
#FOS\UserBundle\Model\UserInterface: bcrypt Commented out to try the following alternative to give password migrating log in
FOS\UserBundle\Model\UserInterface: { id: custom-password-encoder }
BCryptPasswordEncoder (standard FOSUserBundle):
class BCryptPasswordEncoder extends BasePasswordEncoder
{
/* .... */
/**
* {#inheritdoc}
*/
public function encodePassword($raw, $salt)
{
if ($this->isPasswordTooLong($raw)) {
throw new BadCredentialsException('Invalid password.');
}
$options = array('cost' => $this->cost);
if ($salt) {
// Ignore $salt, the auto-generated one is always the best
}
return password_hash($raw, PASSWORD_BCRYPT, $options);
}
/**
* {#inheritdoc}
*/
public function isPasswordValid($encoded, $raw, $salt)
{
return !$this->isPasswordTooLong($raw) && password_verify($raw, $encoded);
}
}
LegacyPasswordEncoder:
namespace AppBundle\Security;
use Symfony\Component\Security\Core\Encoder\BasePasswordEncoder;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
class LegacyPasswordEncoder extends BasePasswordEncoder
{
/**
* {#inheritdoc}
*/
public function encodePassword($raw,$salt)
{
if ($this->isPasswordTooLong($raw)) {
throw new BadCredentialsException('Invalid password.');
}
list($salt1,$salt2) = explode(",",$salt);
return sha1($salt1.$raw.$salt2);
}
/**
* {#inheritdoc}
*/
public function isPasswordValid($encoded, $raw, $salt)
{
list($salt1,$salt2) = explode(",",$salt);
return !$this->isPasswordTooLong($raw) && $this->comparePasswords($encoded,sha1($salt1.$raw.$salt2));
}
}
The solution to your problem is to use the Symfony feature allowing to change the password hashing algorithm dynamically based on the user: https://symfony.com/doc/current/cookbook/security/named_encoders.html
This way, you can mark any non-migrated user as using a legacy algorithm. Then, when updating the password, you would reset the algorithm being used before saving the user, so that the new password is hashed using the new stronger algorithm
Start by mapping your user class to the desired encoder:
security:
hide_user_not_found: false
encoders:
Cerad\Bundle\UserBundle\Entity\User: # Replace with your user class
id: cerad_user.user_encoder # Replace with the service id for your encoder
That should be enough to get your encoder plugged in.
Then you need to actually write your custom encoder by extending BCryptPasswordEncoder and override the isPasswordValid method. And of course create a service for it. Lots to learn.
How to call BcryptPasswordEncorder followed by LegacyPasswordEncoder? You don't. At least not directly. Symfony does not have a chain password encoder. Instead, write your own encoder and implement the chaining yourself.
class MyEncoder extends BCryptPasswordEncoder
{
function isPasswordValid($encoded,$raw,$salt)
{
// Check the bcrypt
if (parent::isPasswordValid($encoded,$raw,$salt)) return true;
// Copied from legacy
list($salt1,$salt2) = explode(",",$salt);
return
!$this->isPasswordTooLong($raw) &&
$this>comparePasswords($encoded,sha1($salt1.$raw.$salt2));
And make sure you define your encoder under services and not parameter. Also be sure to pass the cost (default is 13) as a constructor argument.

Tx_Extbase_Domain_Repository_FrontendUserRepository->findAll() not working in typo3 4.5.30?

I am trying to run a simple query off of the Tx_Extbase_Domain_Repository_FrontendUserRepository. I cannot get anything to work except findByUid(), not even findAll().
In my controller I have this code which seems to work:
/**
* #var Tx_Extbase_Domain_Repository_FrontendUserRepository
*/
protected $userRepository;
/**
* Inject the user repository
* #param Tx_Extbase_Domain_Repository_FrontendUserRepository $userRepository
* #return void */
public function injectFrontendUserRepository(Tx_Extbase_Domain_Repository_FrontendUserRepository $userRepository) {
$this->userRepository = $userRepository;
}
/**
* action create
*
* #param Tx_BpsCoupons_Domain_Model_Coupon $newCoupon
* #return void
*/
public function createAction(Tx_BpsCoupons_Domain_Model_Coupon $newCoupon) {
...... some code .....
$user = $this->userRepository->findByUid(($GLOBALS['TSFE']->fe_user->user[uid]));
$newCoupon->setCreator($user);
...... some code .....
}
but in another function I want to look up a user not by uid but by a fe_users column called vipnumber (an int column) so I tried
/**
* check to see if there is already a user with this vip number in the database
* #param string $vip
* #return bool
*/
public function isVipValid($vip) {
echo "<br/>" . __FUNCTION__ . __LINE__ . "<br/>";
echo "<br/>".$vip."<br/>";
//$ret = $this->userRepository->findByUid(15); //this works!! but
$query = $this->userRepository->createQuery();
$query->matching($query->equals('vip',$vip) );
$ret = $query->execute(); //no luck
.................
and neither does this
$ret = $this->userRepository->findAll();
How can one work but not the others? In my setup I already put
config.tx_extbase.persistence.classes.Tx_Extbase_Domain_Model_FrontendUser.mapping.recordType >
which seems to be necessary for the fiondByUid to work, is i t preventing the other from working?
I am using typo3 v 4.5.30 with extbase 1.3
Thanks
If $this->userRepository->findByUid(15); works, there is no reason why $this->userRepository->findAll(); should not. However $this->userRepository->findAll(); returns not a single Object but a collection of all objects, so you have to iterate over them.
If you add a column to the fe_users, you have to add it to TCA and to your extbase model (you need a getter and a setter), too! After that you can call findByProperty($property) in your repository. In your case that would be
$user = $this->userRepository->findByVipnumber($vip);
This will return all UserObjects that have $vip set as their Vipnumber. If you just want to check if that $vip is already in use, you can call
$user = $this->userRepository->countByVipnumber($vip);
instead. Which obviously returns the number of Users that have this $vip;
You never use $query = $this->createQuery(); outside your Repository.
To add the property to the fronenduser Model you create your own model Classes/Domain/Model/FronendUser.php:
class Tx_MyExt_Domain_Model_FrontendUser extends Tx_Extbase_Domain_Model_FrontendUser {
/**
* #var string/integer
*/
protected $vipnumber;
}
Add a getter and a setter. Now you create your own FrontendUserRepository and extend the extbase one like you did with the model. You use this repository in your Controller. Now you're almost there: Tell Extbase via typoscript, that your model is using the fe_users table and everything should work:
config.tx_extbase {
persistence{
Tx_MyExt_Domain_Model_FrontendUser{
mapping {
tableName = fe_users
}
}
}
}
To disable storagePids in your repository in general, you can use this code inside your repository:
/**
* sets query settings repository-wide
*
* #return void
*/
public function initializeObject() {
$querySettings = $this->objectManager->create('Tx_Extbase_Persistence_Typo3QuerySettings');
$querySettings->setRespectStoragePage(FALSE);
$this->setDefaultQuerySettings($querySettings);
}
After this, your Querys will work for all PIDs.
I didn't have the opportunity to work with frontend users yet, so I don't know if the following applies in this case:
In a custom table I stumbled uppon the fact, that extbase repositories automatically have a look at the pids stored in each entry and check it against a set storage pid (possibly also the current pid if not set). Searching for a uid usually means you have a specific dataset in mind so automatic checks for other values could logically be ignored which would support your experiences. I'd try to set the storage pid for your extension to the place the frontend users are stored in ts-setup:
plugin.[replace_with_extkey].persistence.storagePid = [replace_with_pid]

Multiple identity properties for authentication in ZF2 with Doctrine 2

I have login form with input text fields:
Group Name
User Name
User Password
I have two tables
groups
id
name
users
id
name
group_id
I have its mapping entities and associations.
But user name not unique within table users, because different groups can include users with equal names. Therefore i need:
find group by name in table groups
find user by name in table users with condition where group_id=<group_id>
How to do it correctly in Zend Framework 2 using Doctrine 2?
All official documentation and examples depict situation, where identity property is single column (example).
Sorry for my bad language. Thanks.
Instead of making my own implementation of Doctrine's authentication services i decide to implement it via form validation inside isValid() method of my authentication form.
Example:
<?php
namespace My\Form\Namespace;
use Zend\Form\Form;
use Zend\ServiceManager\ServiceLocatorInterface;
use Zend\InputFilter\InputFilterProviderInterface;
class Auth extends Form implement InputFilterProviderInterface
{
protected $_em;
public function __construct(ServiceLocatorInterface $sm)
{
parent::__construct('auth');
// inject Doctrine's Entity Manager
$this->_em = $sm->get('Doctrine\ORM\EntityManager');
// login field
$this->add(...);
// password field
$this->add(...);
// group_name field
$this->add(...);
}
public function getInputFilterSpecification()
{
//Input filter specification here
...
}
public function isValid()
{
/*
* input filter validations
*/
if (!parent::isValid())
return false;
/*
* group exists validation
*/
$group = $this->_em
->getRepository('<Group\Entity\Namespace>')
->findOneBy(array(
'name' => $this->get('group_name')->getValue(),
));
if (!$group){
$this->get('group_name')
->setMessages(array(
'Group not found',
));
return false;
}
/*
* user exists validation
*/
$user = $this->_em
->getRepository('<User\Entity\Namespace>')
->findOneBy(array(
'group_id' => $group->getId(),
'name' => $this->get('login')->getValue(),
));
if (!$user){
/*
* It's not good idea to tell that user not found,
* so let it be password error
*/
$this->get('password')
->setMessages(array(
'Login or password wrong',
));
return false;
}
/*
* password validation
*/
$password = $this->get('password')->getValue();
// assume that password hash just md5 of password string
if (md5($password) !== $user->getPassword()){
$this->get('password')
->setMessages(array(
'Login or password wrong',
));
return false;
}
return true;
}
}
Inside controller it is enough to call $form->isValid() to make sure that user entered correct authentication data.
I have the same problem.
I have to do two authentications in same application, because my boss doesn't wanna two databases. So, I had to make two user tables and two login pages.
One route to admin -> /admin/login
And the front-end for other users -> /login
I've tried to put on more authenticate in doctrine authentication array but it didn't work.
I think I'll open a issue on doctrine github page.

Doctrine2 ArrayCollection

Ok, I have a User entity as follows
<?php
class User
{
/**
* #var integer
* #Id
* #Column(type="integer")
* #GeneratedValue
*/
protected $id;
/**
* #var \Application\Entity\Url[]
* #OneToMany(targetEntity="Url", mappedBy="user", cascade={"persist", "remove"})
*/
protected $urls;
public function __construct()
{
$this->urls = new \Doctrine\Common\Collections\ArrayCollection();
}
public function addUrl($url)
{
// This is where I have a problem
}
}
Now, what I want to do is check if the User has already the $url in the $urls ArrayCollection before persisting the $url.
Now some of the examples I found says we should do something like
if (!$this->getUrls()->contains($url)) {
// add url
}
but this doesn't work as this compares the element values. As the $url doesn't have id value yet, this will always fail and $url will be dublicated.
So I'd really appreciate if someone could explain how I can add an element to the ArrayCollection without persisting it and avoiding the duplication?
Edit
I have managed to achive this via
$p = function ($key, $element) use ($url)
{
if ($element->getUrlHash() == $url->getUrlHash()) {
return true;
} else {
return false;
}
};
But doesn't this still load all urls and then performs the check? I don't think this is efficient as there might be thousands of urls per user.
This is not yet possible in a "domain driven" way, ie. just using objects. You should execute a query to check for the existance:
SELECT count(u.id) FROM User u WHERE ?1 IN u.urls AND u.id = ?2
With Doctrine 2.1 this will be possible using a combination of two new features:
Extra Lazy Collections
#IndexBy for collections, so you would define #OneToMany(targetEntity="Url", indexBy="location")
ExtraLazy Collection Support for index by using ->contains().
Points 1 and 2 are already implemented in Doctrine 2 master, but 3 is still missing.
You should try using the exists method on the collection and manually compare values.