Testing Remember me functionality with Behat/Mink - behat

I am trying to test a remember me functionality with Behat and Mink in a Symfony2 project. However, my approach is not working.
I tried the following:
#behat.yml
Scenario: Checking Remember me
Given I am on "/"
When I fill in "username" with "john"
And I fill in "password" with "john"
And I check "remember_me"
And I press "Login"
Then I should be logged in
When I restart the browser
Then I should be logged in
Scenario: Not Checking Remember me
Given I am on "/"
When I fill in "username" with "john"
And I fill in "password" with "john"
And I press "Login"
Then I should be logged in
When I restart the browser
Then I should be logged out
My feature context contains (among others) the following methods:
#FeatureContext.php
/**
* #Then /^I should be logged in$/
*/
public function iShouldBeLoggedIn()
{
$this->assertElementOnPage('.user-area');
}
/**
* #Given /^I should be logged out$/
*/
public function iShouldBeLoggedOut()
{
$this->assertElementNotOnPage('.user-area');
}
/**
* #When /^I restart the browser$/
*/
public function iRestartTheBrowser()
{
$driver = $this->getSession()->getDriver();
$session = new Session($driver);
$session->start();
$session->visit('/');
}
The problem lies within iRestartTheBrowser(). This is not doing what it is supposed to do. I am looking for a way to clear session data but keep cookies. Any help?

I think your spec may be better worded as
Scenario: Checking Remember me
Given I have logged in before and selected remember me
When I visit "some protected web page"
Then I should be logged in
The Given can set state for remember me, and perhaps the context for this can help you isolate the need to reset the session?
The above is also easier to read for laypeople and developers alike

Related

How to use Behat in Cake Php?

I have this feature
Feature: User accesses Appointments Dashboard
Scenario:
Given I am on "User Dashboard Page"
And I should see "APPOINTMENTS"
And I should see "PRE-ENROLMENTS"
When I press "APPOINTMENTS"
Then I am on "Appointments Dashboard"
And I should see "Booking Reference"
And I should see "Book Another Appointment"
And I have these steps
--- FeatureContext has missing steps. Define them with these snippets:
/**
* #Given I am on :arg1
*/
public function iAmOn($arg1)
{
throw new PendingException();
}
/**
* #Given I should see :arg1
*/
public function iShouldSee($arg1)
{
throw new PendingException();
}
/**
* #When I press :arg1
*/
public function iPress($arg1)
{
throw new PendingException();
}
How do I complete the steps and run it with Behat in Cake Php ?
So, that my tests pass ??
If you want access to yuor CakePHP internals, then you'll probably want to have an instance of CakePHP application in your Context (e.g.
App\Application or Cake\Http\Server, look at your index.php on it's done). Then, hopefully you'll be able to access all of the usual application internals via default Service Container of CakePHP.

How do i use Behat with Mink and WebApiContext?

The project i'm working on has a api behind a login.
I'm using behat with mink to login:
Scenario: Login
Given I am on "/login/"
And I should see "Login"
When I fill in "_username" with "test"
And I fill in "_password" with "test"
And I press "_submit"
Then I should be on "/"
This works..
However, the login session is not stored whenever i want to do the following using the WebApiContext:
Scenario: Getting the list of pages
When I send a GET request to "/api/pages.json"
Then print response
I'm using both scenarios in the same feature. My FeatureContext class looks something like this:
class FeatureContext extends MinkContext
{
public function __construct(array $parameters)
{
$context = new WebApiContext($parameters['base_url']);
$context->getBrowser()->getClient()->setCookieJar(new \Buzz\Util\CookieJar());
$this->useContext('web', $context);
}
}
I added the cookiejar idea from this issue without success.. When i print the response i just see the HTML page from the login screen..
Does anyone have any idea if i'm going at this totally the wrong way or am i somewhat in the right direction?
I am successfully using the same method. I don't think there's a standard way of doing this. As far as you understand the cookie basics you should be able to implement the solution.
In a common scenario, a client sends an authentication request to the server with some credentials, the servers validates it, starts an authenticated session and sends back a cookie with that session id. All following requests contain that id, so the server can recognise the callee. A specific header can be used instead of the cookie, or a database can be used instead of the session, but the principle is the same and you can (relatively) easily simulate it with Mink.
/**
* Start a test session, set the authenticated user and set the client's cookie.
*
* #Given /^I am signed in$/
*/
signIn()
{
session_start();
$_SESSION['user'] = 'jos';
$this->getSession()->getDriver()->setCookie(session_name(), session_id());
session_commit();
}
The above step definition (Behat 3) is the basics of it, you manually create the authenticated session and set to the client it's id. That must be also what the other example illustrates.
PHP's sessions can be problematic when you start doing more complex things and there are a couple of big underwater rocks with this solution. If you want to run assertions from both perspectives (the client and the server) you might often need to have your sessions synced. This can be done by updating the cookie before all Mink steps and reloading the session after.
/**
* #beforeStep
* #param BeforeStepScope $scope
*/
public function synchroniseClientSession(BeforeStepScope $scope)
{
// Setup session id and Xdebug cookies to synchronise / enable both.
$driver = $this->getSession()->getDriver();
// Cookie must be set for a particular domain.
if ($driver instanceof Selenium2Driver && $driver->getCurrentUrl() === 'data:,') {
$driver->visit($this->getMinkParameter('base_url'));
}
// Also enables the debugging support.
$driver->setCookie(session_name(), session_id());
$driver->setCookie('XDEBUG_SESSION', 'PHPSTORM');
}
/**
* #afterStep
* #param AfterStepScope $scope
*/
public function synchroniseServerSession(AfterStepScope $scope)
{
$driver = $this->getSession()->getDriver();
// Only browser kit driver, only initiated requests, only not repeating requests.
if (!$driver instanceof BrowserKitDriver) {
return;
} elseif (($request = $driver->getClient()->getRequest()) === null) {
return;
} elseif ($request === self::$request) {
return;
}
// Your logic for reloading the session.
self::$request = $request;
}
The biggest problem I had was the session reloading. This might be due to my framework of choice, which I doubt. The very first code snippet has session_commit(), which saves and closes the session. In theory in the following step definitions you must be able to session_id(/* session id from the cookie… */); and session_start();, but in practice that didn't work and no session data was actually loaded from the file, though the session did start. To solve this I created a custom session manager with reload() method using session save handler.
Second problem is where you cannot simply close the session without either writing it or destroying it (the support is added in PHP 5.6) on which relies the reloading itself. I reinvented the wheel with a flag for the session manager which tells it whether to write or just to close it.
:)

Laravel 4 Auth::attempt($userdata, false) authentication is still kept

Recently I have decided to add a "remember me" feature to my Laravel 4 app.
Appropriate method with syntaxis was found:
Auth::attempt(array $credentials = array(), $remember = false)
This was adopted for my needs like so:
Auth::attempt($userdata, Input::has('remember'))
Application kept the Auth session, and the user was authenticated even after browser was closed.
Although, I have found out that now Laravel always keeps a user authenticated, no matter what state "remember" checkmark is.
I have tried to do:
Auth::attempt($userdata, false)
and
Auth::attempt($userdata,)
User was still authenticated across the browser sessions!!!
Now, since Auth::attempt($userdata) not keeping the auth session, I felt that whenever there is an indications of the second argument in Auth::attempt method, Laravel auto assumes it as "true".
Can anyone clarify that?
EDIT:
To make it a super clear to everyone, I will list the steps to recreate this behaviour:
Logout of the app Auth::logout();
Login again Auth::attempt($userdata, false)
Close and open the browser
Go to the app url.
Application is loaded authenticated
This is my first question here, so please, be patient with me :)
EDIT : OP made clear he called Auth::logout() properly, so answer is edited to include the "Real" answer.
Set lifetime value in app/config/session/php to 0 to make cookie clear on browser close.
Previous answer
This is the login method in Illuminate\Auth\Guard (Which is facaded to Auth) class, which is eventually called by Auth::attempt().
source : http://laravel.com/api/source-class-Illuminate.Auth.Guard.html#263-291
public function login(UserInterface $user, $remember = false)
{
$id = $user->getAuthIdentifier();
$this->session->put($this->getName(), $id);
// If the user should be permanently "remembered" by the application we will
// queue a permanent cookie that contains the encrypted copy of the user
// identifier. We will then decrypt this later to retrieve the users.
if ($remember)
{
$this->queuedCookies[] = $this->createRecaller($id);
}
// If we have an event dispatcher instance set we will fire an event so that
// any listeners will hook into the authentication events and run actions
// based on the login and logout events fired from the guard instances.
if (isset($this->events))
{
$this->events->fire('auth.login', array($user, $remember));
}
$this->setUser($user);
}
It is clear that even though the cookie is set when $remember is set to true, the cookie itself is not cleared when $remember is set to false or other non-truthy value.
The cookie is cleared when you call Auth::logout() function.

symfony not recognizing an authenticated user

I am trying to implement a login page with symfony2 and for that I am using my own custom user provider.
The problem is when the user enters his credentials, he will not be recognized. By that, I mean the debug bar at the bottom says "You are not authenticated." and $request->getUser() will return null. But the strange thing is that the user will still be allowed to visit the pages that need him to log in.
I don't think the problem is with the authentication, since when I enter a wrong password, I get warned about it, but when I enter the correct one, I get redirected to the first page (but it still says "You are not authenticated.")
Do you know where I should be looking for the problem?
I have attached my security.yml file in this pastebin and routing.yml in this one.
Here is the code for my custom user provider.
And This is the User class definition.
EDIT: Here is the var_dump of my get('security.context')->getToken(). The funny thing is that authenticated is true, but getUser() is still null and the debug bar says I am not authenticated.
object(Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken)#46 (6) {
["credentials":"Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken":private]=>
NULL
["providerKey":"Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken":private]=>
string(11) "system_area"
["user":"Symfony\Component\Security\Core\Authentication\Token\AbstractToken":private]=>
NULL
["roles":"Symfony\Component\Security\Core\Authentication\Token\AbstractToken":private]=>
array(2) {
[0]=>
object(Symfony\Component\Security\Core\Role\Role)#45 (1) {
["role":"Symfony\Component\Security\Core\Role\Role":private]=>
string(10) "ROLE_ADMIN"
}
[1]=>
object(Symfony\Component\Security\Core\Role\Role)#44 (1) {
["role":"Symfony\Component\Security\Core\Role\Role":private]=>
string(9) "ROLE_USER"
}
}
["authenticated":"Symfony\Component\Security\Core\Authentication\Token\AbstractToken":private]=>
bool(true)
["attributes":"Symfony\Component\Security\Core\Authentication\Token\AbstractToken":private]=>
array(0) {
}
}
In order to get the current User you have to use:
$this->get('security.context')->getToken()->getUser();
In order to check if the current User has a certain role use:
$this->get('security.context')->isGranted('ROLE_USER')
I managed to fix the problem. It was because of the dummy code I had used in place of serialization of User objects. I replaced it with real code and problem was gone.

User authentication using CodeIgniter

I have a problem creating authentication part for my application.
Below is the simplified version of my controllers.
The idea is that the MY_controller checks if session with user data exists.
If it doesn’t, then redirects to the index page where you have to log in.
MY_controller.php
class MY_Controller extends Controller {
function __construct()
{
parent::__construct();
$this->load->helper('url');
$this->load->library('session');
if($this->session->userdata('user') == FALSE) {
redirect('index');
} else {
redirect('search');
}
}
}
order.php - main controller
class Orders extends MY_Controller {
function __construct()
{
parent::__construct();
$this->load->helper('url');
$this->load->library('session');
}
function index()
{
// Here would be the code that validates information input by user.
// If validation is successful, it creates user session.
$this->load->view('header.html', $data); // load header
$this->load->view('index_view', $data); // load body
$this->load->view('footer.html', $data); // load footer
}
function search()
{
//different page
}
what is happening is that the browser is telling me that “The page isn’t redirecting properly. Firefox has detected that the server is redirecting the request for this address in a way that will never complete.”
It seems like the redirect() is being looped. I looked at a few other examples of user auth and they were build using similar technique.
When a user is already logged in, it appears you want to redirect them to /search/. The redirect occurs, and the constructor is called again, which recognizes that the user is already logged in, so it redirects them to /search/... you get the idea.
I would start by separating your login logic into it's own controller that doesn't extend from MY_Controller.
Also, note that when not logged in your controller redirects to 'index'. If the Index controller is also based on My_Controller, then it will redirect back to itself (until the user logs in and then Dolph Mathews' answer comes true).
You need to provide a 'safe zone' with no checking/redirecting that provides users with a login form (and note that your login controller/method has to have open access too!)
I tend to pop a gateway method into My_Controller, which is called only by private controllers/methods as required (in the constructor of completely private controllers). I'm sure there must be a better way though, perhaps like a gateway function in your My_Controller (as yours is done) but that filters for the URI path (e.g. allows index; index/login; index/logout etc)