I've just gotten started with Behat and Mink. I'm using MinkExtension with Goutte and Selenium, and also DrupalExtension.
So far, so good. I can load a page, look for various elements, test links, etc.
But I don't see how to check for 404s on various assets - images, especially, but also css and js files.
Any tips or examples would be much appreciated.
When using Goutte web crawler you can do this:
$crawler = $client->request('GET', 'http://your-url.here');
$status_code = $client->getResponse()->getStatus();
if($status_code==404){
// Do something
}
You can try the following methods:
<?php
use Behat\Behat\Context\Context;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Bundle\FrameworkBundle\Client;
class FeatureContext extends WebTestCase implements Context {
/**
* #var Client
*/
private $client;
/**
* #When /^I send a "([^"]*)" request to "([^"]*)"$/
*
* #param $arg1
* #param $arg2
*/
public function iSendARequestTo($arg1, $arg2) {
$this->client = static::createClient();
$this->client->request($arg1, $arg2);
}
/**
* #Then /^the output should contain: "([^"]*)"$/
*
* #param $arg1
*/
public function theOutputShouldContain($arg1) {
$this->assertContains($arg1, $this->client->getResponse()->getContent());
}
/**
* #Then /^the status code should be "([^"]*)"$/
*
* #param $arg1
*/
public function theStatusCodeShouldBe($arg1) {
$this->assertEquals($arg1, $this->client->getResponse()->getStatusCode());
}
}
Source: FeatureContext.php at jmquarck/kate
Please check the following methods from this HelperContext.php (part of CWTest_Behat):
/**
* #Given get the HTTP response code :url
* Anonymous users ONLY.
*/
public function getHTTPResponseCode($url) {
$headers = get_headers($url, 1);
return substr($headers[0], 9, 3);
}
/**
* #Given I check the HTTP response code is :code for :url
*/
public function iCheckTheHttpResponseCodeIsFor($expected_response, $url) {
$path = $this->getMinkParameter('base_url') . $url;
$actual_response = $this->getHTTPResponseCode($path);
$this->verifyResponseForURL($actual_response, $expected_response, $url);
}
/**
* Compare the actual and expected status responses for a URL.
*/
function verifyResponseForURL($actual_response, $expected_response, $url) {
if (intval($actual_response) !== intval($expected_response)) {
throw new Exception("This '{$url}' asset returned a {$actual_response} response.");
}
}
/**
* #Given I should get the following HTTP status responses:
*/
public function iShouldGetTheFollowingHTTPStatusResponses(TableNode $table) {
foreach ($table->getRows() as $row) {
$this->getSession()->visit($row[0]);
$this->assertSession()->statusCodeEquals($row[1]);
}
}
Here are the example scenarios written in Behat which are using the above methods:
#roles #api #regression
Scenario: Verify Anonymous User access to /user/login
Given I am not logged in
Then I check the HTTP response code is 200 for '/user/login'
#roles #api #regression
Scenario: Verify Anonymous User access to /admin
Given I am not logged in
Then I check the HTTP response code is 403 for '/admin'
#roles #api #regression
Scenario: Verify Administrator access to /admin
Given I am logged in as a user with the admin role
And I am on "/admin"
Then the response status code should be 200
It's possible to use Restler, a micro framework which can help with RESTful API testing in Behat. It support behavior Driven API testing using Behat and Guzzle.
Check the following example:
Scenario: Saying
When I request "/examples/_001_helloworld/say"
Then the response status code should be 404
And the response is JSON
And the type is "array"
Related
Recently I have made some code changes to store sessions in Database using PdoSessionHandler. I am using Guard Authentication. checkCredentials is working fine is working fine, insert into "sessions" table is also working fine. But the Authentication token in the session is lost after /login_check redirect.
Authentication token is getting stored in the serialized format under "_security_secured_area" in the session and the session is also saved in the DB but after the redirect from /login_check to /login_redirect session is available with the same id but the auth token details are missing. Probably it is not able to populate auth details from the DB.
Here is my packages/security.yaml
firewalls:
# disables authentication for assets and the profiler, adapt it according to your needs
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
secured_area:
pattern: ^/
anonymous: ~
guard:
authenticators:
- App\Security\LoginFormAuthenticator
logout:
path: _logout
target: _public_signin
logout_on_user_change: true
remember_me:
secret: '%kernel.secret%'
lifetime: 2592000 # 30 days in seconds
path: /
domain: ~
remember_me_parameter: _stay_signedin
# by default, the feature is enabled by checking a
# checkbox in the login form (see below), uncomment the
# following line to always enable it.
#always_remember_me: true
token_provider: token_service
Here is my gurardAuthenticator:
/**
* Override to change what happens after successful authentication.
*
* #param Request $request
* #param TokenInterface $token
* #param string $providerKey
*
* #return RedirectResponse
*/
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
/** #var User $user */
$user = $token->getUser();
if ($user->getNewUser() === true) {
$url = '_onboard';
} elseif ($user->getResetPass() === true) {
$url = '_change_temp_password';
} else {
$url = '_login_redirect';
}
//$request->getSession()->save();
// MAS: TODO Add Audit probably in listener
return new RedirectResponse($this->urlGenerator->generate($url));
}
After AuthenticationSuccess it automatically redirects to loginReditrectAction() in SecurityController.php but here PostAuthenticationGuardToken is lost, AuthenticationEvent is returning AnonymousToken.
Another observation I found when I printed session in loginRedirectAction() in SecurityContrller.php is "_security_secured_area" in session data is missing.
#session: Session {#149 ▼
#storage: NativeSessionStorage {#148 ▶}
-flashName: "flashes"
-attributeName: "attributes"
-data: &2 array:2 [▼
"_sf2_attributes" => &1 array:4 [▼
"_csrf/https-kinetxx" => "rvR8Rr2UcDM_-y16ehk_jgYvMREJ8mTNouYCT16RtfY"
"_security.last_username" => "ktx_provider"
"userTimeZone" => "America/Chicago"
"practiceTimeZone" => "America/New_York"
]
"_symfony_flashes" => &3 []
]
My SecurityController.php
/**
* #Route("/login_redirect", name="_login_redirect")
*
* #param Request $request
*
* #return RedirectResponse
*/
public function loginRedirectAction(Request $request)
{
dump($request);
dump($this->get('security.authorization_checker'));
die;
}
Can someone help me resolving this?
I had this problem and the solution was to change my provider entity from
implements UserInterface, \Serializable
to
implements AdvancedUserInterface, \Serializable, EquatableInterface
and adding the needed methods: isEqualTo(UserInterface $user), isAccountNonExpired(), isAccountNonLocked(), isCredentialsNonExpired(), isEnabled()
Im using Symfony3 and i have this route:
/**
* Recive a file
*
* #Route("/get/file", name="get-file")
* #Method("POST")
* #param Request $request
* #return Response
*/
public function getFileAction (Request $request) {
foreach($request->files as $key => $uploadedFile) {
$name = "westSummary".date("Y-m-d")."_".$key.".csv";
$file = $uploadedFile->move($destiny, $name); //Save the file in server, Where $destiny says.
}
return $this->json(array(
'status' => 'success',
'code' => 200,
'data' => $request,
));
}
And i create a Collection in Postman calling this route and that return the JSON object correctly:
But i cant test if the CSV arrives to the request object.
I have tryed this:
But the Response is the same in both cases
- How can i send a CSV to and API using POSTMAN?
If you’re trying to do this via the collection runner, this functionality isn’t supported.
That collection runner option relates to the data file you want to use to populate the request variables and not a file upload.
This is a issue being recently discussed on the Postman GitHub account.
https://github.com/postmanlabs/postman-app-support/issues/3137
To upload a file in the request - You can do this by selecting the form-data option in the request body. There is a text/file dropdown - By selecting file you will get the option to choose one for the request.
One other way that you can achieve this using a collection is by installing Newman and using this to create a script to upload the file during the test run.
I could really use some help with this.
BACKGROUND:
I've got phpBB 3.0 installed and have working external authentication from my own site's database. My working is an implementation of this excellent worked example: https://github.com/nzeyimana/PhpBBAuthDbExt/blob/master/auth_dbext.php
I now want to upgrade my Forum to 3.2 (current version).
PROBLEM:
Trying to follow the example in the documentation https://area51.phpbb.com/docs/dev/32x/extensions/tutorial_authentication.html#authentication-providers and also phpBB community/viewtopic.php?f=461&t=2272371
I've copied the class file from the example documentation, calling it db2.php and placed in "ext/acme/demo/auth/provider/"
I've also copied the service file from the example documentation, calling it services.yml and placed in "ext/acme/demo/config/"
Copies of both file contents at bottom below.
According to the documentation, I should then see db2 in the list of authentication methods in the Authentication part of Access Control Panel (ACP) - but nothing appears. I've flushed the forum cache, flushed my browsers cache etc, to no avail.
Am I missing something? Any help REALLY appreciated.
Kevin
This is the content of the db2.php file:
#
<?php
namespace acme\demo\auth\provider;
/**
* Database authentication provider for phpBB3
*
* This is for authentication via the integrated user table
*/
class db2 extends \phpbb\auth\provider\base
{
/** #var \phpbb\db\driver\driver_interface $db */
protected $db;
/**
* Database Authentication Constructor
*
* #param \phpbb\db\driver\driver_interface $db
*/
public function __construct(\phpbb\db\driver\driver_interface $db)
{
$this->db = $db;
}
/**
* {#inheritdoc}
*/
public function login($username, $password)
{
// Auth plugins get the password untrimmed.
// For compatibility we trim() here.
$password = trim($password);
// do not allow empty password
if (!$password)
{
return array(
'status' => LOGIN_ERROR_PASSWORD,
'error_msg' => 'NO_PASSWORD_SUPPLIED',
'user_row' => array('user_id' => ANONYMOUS),
);
}
if (!$username)
{
return array(
'status' => LOGIN_ERROR_USERNAME,
'error_msg' => 'LOGIN_ERROR_USERNAME',
'user_row' => array('user_id' => ANONYMOUS),
);
}
$username_clean = utf8_clean_string($username);
$sql = 'SELECT user_id, username, user_password, user_passchg, user_pass_convert, user_email, user_type, user_login_attempts
FROM ' . USERS_TABLE . "
WHERE username_clean = '" . $this->db->sql_escape($username_clean) . "'";
$result = $this->db->sql_query($sql);
$row = $this->db->sql_fetchrow($result);
$this->db->sql_freeresult($result);
// Successful login... set user_login_attempts to zero...
return array(
'status' => LOGIN_SUCCESS,
'error_msg' => false,
'user_row' => $row,
);
}
}
#
This is the content of the services.yml file:
#
services:
auth.provider.db2:
class: acme\demo\auth\provider\db2
arguments:
- '#dbal.conn'
tags:
- { name: auth.provider }
#
Unfortunately, the documentation is missing an important part - every extension should have its composer.json file in order to identify the extension - link.
You can refer to OneAll phpBB extension to see its structure and code. Use it as an example.
Once you have your composer.json, you should see you extension in the extension management list. Then enable your extension and you should see it in the Authentication section in your Access Control Panel (ACP)
I hope this helps.
I have a feature file as below
Feature: Test send API request
In order to test my API
As a Tester
I want to be able to perform HTTP request
Scenario:Sending GET request to activate user after registration api to verify whether the response code is 403 when 'X-Auth-Token' is missing
When I have a request "GET /api/activateuser?token=:tokenhash"
And I set the "Accept" header to "application/json"
And I set the "X-Auth-Token" header to "0125ee8dfe42bafbec95aa0d2676c91d8a780715b76504cf798aae6e74c08a30"
.
.
Scenario:Sending GET request to activate user after registration api to verify whether the response code is 403 when 'X-Auth-Token' is invalid
When I have a request "GET /api/activateuser?token=:tokenhash"
And I set the "Accept" header to "application/json"
And I set the "X-Auth-Token" header to "0125ee8dfe42bafbec95aa0d2676c91d8a780715b76504cf798aae6e74c08a30"
.
.
Scenario:Sending GET request to activate user after registration api to verify whether the response code is 404 when userid is invalid
When I have a request "GET /api/activateuser?token=:tokenhash"
And I set the "Accept" header to "application/json"
And I set the "X-Auth-Token" header to "0125ee8dfe42bafbec95aa0d2676c91d8a780715b76504cf798aae6e74c08a30"
.
.
In the request 'X-Auth-Token' parameter will be same for all scnerios, which will not change frequently. So I was thinking of setting it to some variable and use that variable in the scenarios. But havent found any method to do this in behat. It is okay evenif we can set the value in behat.yml and use it in the scenario, but even that was not possible.
Also i have more than one parameters that needed to be set like this.
So is there any method to set the value once and resuse it in every scenario?
You can use combination of two.
A Background where you run all common steps for all scenarios.
A BeforeFeature hook that prepares your current test scope.
What happens below is this.
#BeforeScenario tag runs prepare() method before everything else to set your variable for current feature session.
Step definitions under Background task run before each scenarios so you don't have to duplicate them in each scenarios.
NOTE: If your X-Auth-Token won't change frequently then just hard-code the value in your FeatureContext file and don't implement step 2 above at all. My example is there to give you an idea of some useful features of Behat.
EXAMPLE
Adjust it for your need!
FeatureContext
namespace Your\Bundle\Features\Context;
use Behat\Behat\Hook\Scope\BeforeScenarioScope;
...
class FeatureContext ...
{
private static $xAuthToken;
/**
* #BeforeFeature
*/
public static function prepare()
{
self::setXAuthToken();
}
private static function setXAuthToken()
{
self::$xAuthToken = 123;
}
/**
* #Given /^I set the header "([^"]*)" to "([^"]*)"$/
*/
public function iSetTheHeader($header, $value)
{
// Do whatever you want
}
/**
* #Given /^I send "([^"]*)" request to "([^"]*)"$/
*/
public function iSendRequest($method, $url)
{
// Do whatever you want
}
/**
* #Given /^the X-Auth-Token is available$/
*/
public function theXAuthTokenIsAvailable()
{
echo self::$xAuthToken;
}
}
Feature file
Feature: Shared token
Background: I am common to all scenarios
Given I set the header "Accept" to "application/json"
When I send "GET" request to "/api/hello-world"
Scenario: 1
Given the X-Auth-Token is available
Scenario: 2
Given the X-Auth-Token is available
RESULT
I'm working curently on a Zend2 project where there is an authentifaction system for the whole website, it was fine until we had to develop a module which is an public web service.
I would like to know if it's possible to allow users to access to a specific module/routing of Zend 2 ?
The Zend\Authentication\Adapter\Http provides an easy way for Apache like authentication in Zend Framework 2 applications.
It comes with two implementations Basic and Digest HTTP Authentication, which can be combined with two sub components - the class itself or a FileResolver. We are going to use the FileResolver to read the stored credentials and compare them to the submitted values.
First thing first. There are few important things to know.
Create a folder with name auth in MODULE_NAME/config/. Inside that folder create two files basic.txt and digest.txt. The file formats are smillar to Apache .htpasswd files.
Basic - <username>:<realm>:<credentials>, here credentials should be written in clear text, e.g.: basic:authentication:plaintextpassword.
Digest - <username>:<realm>:<credentials>, where <credentials> is the md5 hash of all 3 parts, e.g.: digest:authentication:dc45122ef294d83e84a8b5a3a6c5356b
In the same module, where we have just created our auth folder, open module.config.php file and place this code.
The code tells us which authentication schemes we accept, the realm (must be the same as the realm in the basic/digest.txt files, digest_domains (only when we use digest authentication) is the URL(s) where we want to apply the same valid information, nonce_timeout sets the number of seconds for which the nonce is valid.
/**
* Used for basic authentication
*/
'authentication_basic' => [
'adapter' => [
'config' => [
'accept_schemes' => 'basic',
'realm' => 'authentication',
'nonce_timeout' => 3600,
],
'basic' => __DIR__.'/auth/basic.txt',
],
],
/**
* Used for digest authentication
*/
'authentication_digest' => [
'adapter' => [
'config' => [
'accept_schemes' => 'digest',
'realm' => 'authentication',
'digest_domains' => '/learn-zf2-authentication/digest',
'nonce_timeout' => 3600,
],
'digest' => __DIR__.'/auth/digest.txt',
],
]
LearnZF2Authentication\Factory\BasicAuthenticationAdapterFactory
$config = $serviceLocator->get('Config');
$authConfig = $config['authentication_basic']['adapter'];
$authAdapter = new HttpAdapter($authConfig['config']);
$basic = new FileResolver();
$basic->setFile($authConfig['basic']);
$authAdapter->setBasicResolver($basic);
return $authAdapter;
LearnZF2Authentication\Factory\DigestAuthenticationAdapterFactory
$config = $serviceLocator->get('Config');
$authConfig = $config['authentication_digest']['adapter'];
$authAdapter = new HttpAdapter($authConfig['config']);
$digest = new FileResolver();
$digest->setFile($authConfig['digest']);
$authAdapter->setDigestResolver($digest);
return $authAdapter;
These are the codes we use to pass the authentication information
Module.php
/**
* #var MvcEvent $e
*/
$request = $e->getRequest();
$response = $e->getResponse();
$view = $e->getApplication()->getMvcEvent()->getViewModel();
$sm = $e->getApplication()->getServiceManager();
$authAdapter = $sm->get('LearnZF2Authentication\BasicAuthenticationAdapter');
/**
* Not HTTP? Stop!
*/
if (!($request instanceof Http\Request && $response instanceof Http\Response)) {
return;
}
/**
* Call the factory class and try to authenticate
*/
if ($e->getRouteMatch()->getParam('action') == 'digest') {
$authAdapter = $sm->get('LearnZF2Authentication\DigestAuthenticationAdapter');
}
$authAdapter->setRequest($request);
$authAdapter->setResponse($response);
if($e->getRouteMatch()->getParam('action') == 'basic' || $e->getRouteMatch()->getParam('action') == 'digest') {
$result = $authAdapter->authenticate();
/**
* Pass the information to the view and see what we got
*/
if ($result->isValid()) {
return $view->identity = $result->getIdentity();
} else {
/**
* Create a log function or just use the one from LearnZF2.
* Also make sure to redirect to another page, 404 for example
*/
foreach ($result->getMessages() as $msg) {
return $view->authProblem = $msg;
}
}
}
This is the code we use to pass the authentication information
One last important thing to note is that you must include a special header called Authorization n your request, replace :
RewriteRule ^(.*)$ %{ENV:BASE}index.php [NC,L]
with
PHP compiled as CGI does not support apache_response_headers function, but we need this header in order to do basic HTTP authtentication when running with CGI or FastCGI.
RewriteRule ^(.*)$ %{ENV:BASE}index.php [E=HTTP_AUTHORIZATION:% {HTTP:Authorization},L,NC]
and add in top of public/index.php
if (isset($_SERVER["REDIRECT_HTTP_AUTHORIZATION"])) {
$_SERVER["HTTP_AUTHORIZATION"] = $_SERVER["REDIRECT_HTTP_AUTHORIZATION"];
}
Some things to note. The auth folder as well the authentication code from module.config.php is best to be placed in your main config folder, where the global|local.php files are and excluded from commits.