ZF2 Authentication - authentication

i am developing an application using ZF2. I have done the user authentication with username & password. But, i would like to check an additional column(example: status) in authentication.
I have done the following codes.
public function authenticate()
{
$this->authAdapter = new AuthAdapter($this->dbAdapter,
'usertable',
'username',
'password'
);
$this->authAdapter->setIdentity($this->username)
->setCredential($this->password)
->setCredentialTreatment('MD5(?)');
$result = $this->authAdapter->authenticate();
return $result;
}
How can i check the column 'status' in authentication?
Note: status value should be 1.
Thanks.

When I was building my authentication using zf2 and doctrine, I have created authorization plugin and customized this adapter for passing extra column for authentication.
You probably need to go on similar directions.
$adapter = new AuthAdapter($db,
'users',
'username',
'password',
'MD5(?)'
);
// get select object (by reference)
$select = $adapter->getDbSelect();
$select->where('active = "TRUE"');
// authenticate, this ensures that users.active = TRUE
$adapter->authenticate();
Reference
After changes your code should look something like this.
public function authenticate()
{
$this->authAdapter = new AuthAdapter($this->dbAdapter,
'usertable',
'username',
'password'
);
$select = $this->authAdapter->getDbSelect();
$select->where('status= "1"');
$this->authAdapter->setIdentity($this->username)
->setCredential($this->password)
->setCredentialTreatment('MD5(?)');
$result = $this->authAdapter->authenticate();
return $result;
}

ZF2 provides a another way to handle additional checks using other columns than the ones foreseen for identity and credential thanks to the method getResultRowObject. All columns of usertable in your example are available as properties of the object returned by getResultRowObject(). So you could expand your code with this :
if ($result->isValid()) {
$identityRowObject = $this->authAdapter->getResultRowObject();
$status = $identityRowObject->status;
// do whatever complex checking you need with $status...
}
Regards,
Marc

Related

CakePHP 2 user database/password migration for Cakephp 4

My customer has program which is made with CakePHP 2 (not my project) and he wants to upgrade it to newest CakePHP.
I noticed that there was tutorial for Custom Password Hasher (legacy / sha1 + blowfish) but it has been deprecated since 4.0 and should use authentication plugin.
How i can make that kind of procedure without touching core code which check if password needs rehash and then create new hash on database?
On loadAuthenticator i cannot put different hasher for that.
Yes but old valid user gives on login "Authentication\Authenticator\Result Object ( [_status:protected] => FAILURE_IDENTITY_NOT_FOUND [_data:protected] => [_errors:protected] => Array ( [Password] => Array ( ) ) )"
and newly created user with new has Authentication\Authenticator\Result Object ( [_status:protected] => SUCCESS etc..
I have made that check with that tutorial but how it can be valid if that not "exists"? SQL-query returns 1 row with old users email.
public function login() {
$this->viewBuilder()->setLayout('login');
if($this->request->is(['post','put'])) {
$result = $this->Authentication->getResult();
// regardless of POST or GET, redirect if user is logged in
if ($result->isValid()) {
$authService = $this->Authentication->getAuthenticationService();
// Assuming you are using the `Password` identifier.
if ($authService->identifiers()->get('Password')->needsPasswordRehash()) {
// Rehash happens on save.
$user = $this->Users->get($this->Authentication->getIdentityData('id'));
$user->password = $this->request->getData('password');
$this->Users->save($user);
}
// Redirect to a logged in page
return $this->redirect([
'controller' => 'Pages',
'action' => 'display',
'home'
]);
}
}
}
It will never goes inside "result->isvalid()" with old user.

Cakephp 3.7.x How to retrieve user data? using Authentication component

I am using cakephp 3.7.2 with Authentication component
$user = $this->Authentication->getIdentity();
prints:
object(Authentication\Identity) {
'config' => [
'fieldMap' => [
'id' => 'id'
]
],
'data' => object(App\Model\Entity\User) {
'id' => (int) 1,
'email' => 'aa.aaa#gmail.com',
...
}
}
I have tried $user->data but it doesn't work.
How to print user data?
Authentication Component
So I have figured it out.
In User Entity class
Add use Authentication\IdentityInterface;
and then implement the IdentityInterface.
class User extends Entity implements IdentityInterface
{
blablabla...
yale yale yale ...
Now you can print:
$user = $this->Authentication->getIdentity();
debug($user->id);
As per Authentication component documentation
The identity object is returned by the service and made available in
the request. The object provides a method getIdentifier() that can be
called to get the id of the current log in identity.
You can use this accordingly as below to get the user data:
// Service
$identity = $authenticationService
->getIdentity()
->getIdentifier()
// Component
$identity = $this->Authentication
->getIdentity()
->getIdentifier();
// Request
$identity = $this->request
->getAttribute('identity')
->getIdentifier();
The identity object provides ArrayAccess but as well a get() method to
access data. It is strongly recommended to use the get() method over
array access because the get method is aware of the field mapping.
For eg. to access email and username from the identity you can use the below code.
$identity->get('email'); // to access email
$identity->get('username'); // to access username
Reference link: Authentication -> Docs -> Identity Object
Hope this will help.
I´m using AuthComponent in CakePHP 4.xxx.
I can get User data i.e. in a view with
$user = $this->getRequest()->getAttribute('identity');
I found the Information on: http://gotchahosting.com/blog/category/cakephp/4002624
Maybe it helps someone who is looking for information about this in CakePHP4

the auth_key of yii2 is'nt interference in cookie base

I have problem with auth_key , I have login form and it's work correctly without remember me and with remember me , but I read yii document , in that document wrote about remember me work with id and auth_key for create cookie to stay user in long time , i check the framework code and in there have three parameters (id, auth_key, expire_time()) i save auth_key in user table and it's code here
public function generateAuthKey()
{
$this->auth_key = Yii::$app->security->generateRandomString();
}
public function validateAuthKey($authKey)
{
return $this->getAuthKey() === $authKey;
}
public function getAuthKey()
{
return $this->auth_key;
}
but i have problem , it's if a user login in site and i go to the user table and change the auth_key field , and now if user refresh the page it must be throw out the site because it's auth key is changed , but the user stay login in site , where is problem ?
The main use of auth_key is to authenticate the user by cookie (user don't have to put login data again). When you choose to be remembered at Login, this is how you are remembered. The system has to identify and login you somehow. It won't log out user if u change it.
You can try to change the key yourself in the "ValidateAutney" method, but this will be a bad practice, it is better to set the session time.
'session' => [
'class' => 'yii\mongodb\Session',
'writeCallback' => function($session)
{
return [
'user_id' => Yii::$app->user->id,
'agent' => Yii::$app->request->getUserAgent(),
'ip' => Yii::$app->request->getUserIP(),
'auth_key' => Yii::$app->security->generateRandomString(),
];
}
],
public function getAuthKey()
{
Yii::$app->session->open();
$query = new Query();
$query->select(['auth_key'])
->from('cache')
->where(['id'=> Yii::$app->session->id ]);
$row = $query->one();
return $row['auth_key'];
}

Authentification with HTTP cookie in Selenium using Behat

I would like to create a behat definition to authenticate a user using a cookie.
It works with the Behat BrowserKitDriver, when there is no #javascript tag on the behat scenario.
But it did not work with the Behat Selenium2Driver, when there is the #javascript tag like here.
I used the symfony-demo application for demonstrate my tests.
What's wrong in my definition ?
/**
* #Given I am logged in as :username
*/
public function iAmLoggedInAs($username)
{
$driver = $this->getSession()->getDriver();
$session = $this->kernel->getContainer()->get('session');
$user = $this->kernel->getContainer()->get('security.user.provider.concrete.database_users')->loadUserByUsername($username);
$providerKey = 'secured_area';
$token = new UsernamePasswordToken($user, null, $providerKey, $user->getRoles());
$session->set('_security_'.$providerKey, serialize($token));
$session->save();
if ($driver instanceof BrowserKitDriver) {
$client = $driver->getClient();
$cookie = new Cookie($session->getName(), $session->getId());
$client->getCookieJar()->set($cookie);
} else if ($driver instanceof Selenium2Driver) {
$this->visitPath('/');
} else {
throw new \Exception('Unsupported Driver');
}
$this->getSession()->setCookie($session->getName(), $session->getId());
}
I just want that my last behat test works.
I don't know if I'm clear... ask if not.
If you can do a Pull Request with a fix it will be perfect.
You have a few ways
Option 1. Just Use Mink
If you use Mink, your Context will extend RawWebContext or WebContext so you can access to the Mink Session with getSession().
Then, use setCookie.
As you can see, luckily for us, and this is the point with working with Mink, Cookie Manipulation is supported by many drivers
// ...
$this->visitPath('/');
$this->getSession()->setCookie(
$session->getName(),
$session->getId()
);
Note 1: Mind you, the following are not the same thing:
$session (ref. your question) is an instance of Symfony\Component\HttpFoundation\Session\Session
$this->getSession() returns an instance of Behat\Mink\Session
Note 2: Want to see your cookies?
var_dump($driver->getClient()->getCookieJar());
Option 2. Access Selenium2 Session
Don't hesitate to dive in the code to see how Selenium2 WebDriver Session works.
You will surely find absolute joy and peace of mind.
else if ($driver instanceof Selenium2Driver) {
$this->visitPath('/');
$cookie = array(
"domain" => "", <----- You can even add your domain here
"name" => $session->getName(),
"value" => $session->getId(),
"path" => "/",
"secure" => False
);
$driver->getWebDriverSession()->setCookie($cookie);
}
Note 3: Want to see your cookies?
var_dump($driver->getWebDriverSession()->getAllCookies());
Here you are the code I am using for automatically authenticate as a USER. This allows me saving a lot of time (login test should be made anyway in another feature file):
Sample Scenario into a YOUR_FILE.feature example file:
Scenario: List Backend Products
Given I auto authenticate as "YOUR_USER_NAME"
Then I go to "http://YOUR_SITE/backend/products"
Then I should see "Backend Products Management"
/**
* #Given /^I auto authenticate as "([^"]*)"$/
*/
public function iAutoAuthenticateAs($userName)
{
$user = $this->getContainer()->get('fos_user.user_manager')->findUserByUsername($userName);
$providerKey = $this->getContainer()->getParameter('fos_user.firewall_name');
$token = new UsernamePasswordToken($user, null, $providerKey, $user->getRoles());
$context = $this->getContainer()->get('security.token_storage');
$session = $this->getContainer()->get('session');
$context->setToken($token);
$session->set('_security_'.$providerKey, serialize($token));
$session->save();
$this->getSession()->setCookie($session->getName(), $session->getId());
}

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.