Guzzle 6 is following redirects on local docker server, but not on production server - php-7

I am using Guzzle 6 Http Client to scrape web pages and analyze them from SEO perspective, however interesting thing is, that Guzzle does not follow redirects at all, when being used in production, but code is exatly the same. Here is the snippet I am using to request page and track redirects.
$onRedirect = function (RequestInterface $request, ResponseInterface $response, UriInterface $uri): void {
$this->totalRedirects++;
};
$response = $this->httpClient->request('GET', $url, [
'allow_redirects' => [
'max' => self::MAX_REDIRECTS,
'referer' => true,
'track_redirects' => true,
'on_redirect' => $onRedirect
],
'headers' => [
'User-Agent' => self::USER_AGENT
],
'http_errors' => true
]);
$redirectUrls = $response->getHeader('X-Guzzle-Redirect-History');
$redirectStatuses = $response->getHeader('X-Guzzle-Redirect-Status-History');
foreach ($redirectUrls as $key => $redirectUrl) {
$this->responses[] = new HttpResponse($redirectUrl, $redirectStatuses[$key]);
}
//Save last successful response
$this->responses[] = new HttpResponse($url, $response->getStatusCode());
My redirect middleware is not triggered at all, using this in production and it returns only "307", while in docker I get "307" and "200". This have been tested using samaritans page - (https://www.samaritans.org/)
Both Production and docker are using PHP 7.2 and Guzzle 6

Related

Access blocked by CORS policy No 'Access-Control-Allow-Origin' header is present on the requested resource

Laravel 9.19
Livewire 2.10
Filament 2.0
masbug/flysystem-google-drive-ext 2.2
I am trying to using google drive as a filesystems storage .. every thing works fine so i can store files and open it .. except that the filament can not fetch the stored file and the console log gives me an error
filesystems.php
'google' => [
'driver' => 'google',
'clientId' => "xxxxxxxxxxxx.apps.googleusercontent.com",
'clientSecret' => "xxxxxxxxxxxxxxxxxxxxx",
'refreshToken' => "xxxxxxxxxxxxxxxxxxxxxx",
'folderId' => env('GOOGLE_DRIVE_FOLDER_ID', null),
],
config/cors.php
<?php
return [
'paths' => ['api/*'], //try ['api/*', 'oauth/*'] , [] and ['*'] Nothing work
'allowed_methods' => ['*'],
'allowed_origins' => ['*'],
'allowed_origins_patterns' => [], //try ['*'] Not working
'allowed_headers' => ['*'],
'exposed_headers' => [],
'max_age' => 0,
'supports_credentials' => false, //try true Not working
];
ComplaintResource.php
public static function form(Form $form): Form
{
return $form
->schema([
Section::make('')
->schema([
//.........
FileUpload::make('reply_pdf')
->disk('google')
->acceptedFileTypes(['application/pdf']),
//.......
])->columns(3)
]);
}
the filament input keeps showing loading indicator
console.log
I am trying to make a middleware to solve this .. but nothing happen
Middleware/Cors.php
public function handle(Request $request, Closure $next)
{
$response = $next($request);
$response->headers->set('Access-Control-Allow-Origin', '*');
$response->headers->set('Access-Control-Allow-Methods', 'POST, GET, OPTIONS, PUT, DELETE');
$response->headers->set('Access-Control-Allow-Headers', 'Content-Type, Accept, Authorization, X-Requested-With, Application');
return $response;
}
I tried to add the next code to .htaccess file .. but it didn't work also
.htaccess
<IfModule mod_headers.c>
Header set Access-Control-Allow-Origin "*"
</IfModule>
I am run php artisan config:clear and php artisan cache:clear .. not working
The only thing that worked after install CORS Unblock extension to Chrome browser and enable Access-Control-Allow-Origin from it!

Mock API when performing a WebTestCase and do $client->submitform() in Symfony 5

I have a Test which submits a form. This form usually results in performing doing a external API call. I want to mock that because I'm not interested in the API but in the action.
Whenever I call submitForm the client is still making an external call, but I don't want that.
The test also fails because the api expects a api key which the test does not have.
class SubscriptionControllerTest extends WebTestCase
{
public function testSubscribe(): void
{
$client = static::createClient();
$userRepository = static::$container->get(UserRepository::class);
$testUser = $userRepository->findOneBy(['email' => UserFixtures::$testUser]);
$clientMock = $this->createMock(ApiClient::class);
//replace the api with the mock one
self::$container->set(ApiClient::class, $clientMock);
$client->loginUser($testUser);
$client->request('GET', '/subscription/new');
$client->submitForm('btn-submit', [
'subscribe[firstName]' => 'firstname',
'subscribe[lastName]' => 'lastname'
]);
$this->assertResponseStatusCodeSame(201);
}
}
What am I doing wrong here?
So the problem is described in this post:
https://stackoverflow.com/a/19951344/3679577
TL:DR
2 requests are being done in my test. A GET and a submitForm (POST). First one uses the proper Mock, second request rebuilds the kernel and the mocks are gone.
My solution was to just use 1 request by submitting the form with a POST request. Using the CSRF token manager to generate a csrf token:
public function testSubscribe(): void
{
$client = static::createClient();
$userRepository = static::$container->get(UserRepository::class);
$testUser = $userRepository->findOneBy(['email' => UserFixtures::$testUser]);
$csrfTokenGenerator = $client->getContainer()->get('security.csrf.token_manager');
$apiClient = $this->createMock(ApiClient::class);
$client->getContainer()->set(ApiClient::class, $apiClient);
$client->loginUser($testUser);
$client->request('POST', '/subscription/new', [
'subscribe' => [
"firstName" => 'firstname',
"lastName" => "lastname",
"_token" => $csrfTokenGenerator->getToken('subscribe')->getValue()
]
]);
$this->assertResponseRedirects('/subscription/thankyou');
}

How to get rid of error 422 laravel in a unit test?

So I'm writing unit tests for a laravel 5.7 web app and when I test the login it gives me error 422(I know that it has something to do with invalid data, I just don't know how to fix it)
public function testRegularUserLogin_CreatedRegularUse_ReturnsStoreView()
{
$regularUser = factory( User::class)->create();
$response = $this->json('POST','/login',
[
'email' => $regularUser->email,
'password' => $regularUser->password,
'_token' => csrf_token()
]);
$response->assertStatus(200);
}
I've tried using the csrf token on the header
This is the output that test gives me
You should just mock authentication:
do something like this
public function getFakeClient()
{
$client = factory(App\User::class)->create();
$this->be($client);
Auth::shouldReceive('user')->andReturn($this->client);
Auth::shouldReceive('check')->andReturn(true);
return $this->client;
}
then
$user = $this->getFakeClient();
$user->shouldReceive('posts')->once()->andReturn(array('posts'));
as recommended by Taylor Otwell himself here.

How to set cookie before test in Laravel?

I need to test a specific behaviour based on the presence of a cookie, how do I set a cookie before sending the request (or visiting the page) ? For the moment the following fails, it behaves likes nothing was set.
$this->actingAs($user)
->withSession(['user' => $user, 'profile' => $profile]) ;
#setcookie( 'locale_lc', "fr", time() + 60 * 60 * 24 * 900, '/', "domain.com", true, true) ;
$this->visit('/profile') ;
Or
$cookie = ['locale_lc' => Crypt::encrypt('fr')] ;
$this->actingAs($user)
->withSession(['user' => $user, 'profile' => $profile])
->makeRequest('GET', '/profile', [], $cookie) ;
The problem was in the setting and reading of cookies. Neither #setcookie nor $_COOKIE will work from a testing context. The method makeRequest with a cookie array is the right one.
However !
The reading script (controller, middleware) must be using Laravel's $request->cookie() method and not directly try to access it with $_COOKIE. In my case the cookie needs to be read by another app on our domain so I also had to disable the encryption for that specific cookie, which can be done in EncryptCookies.php
EncryptCookies
<?php
protected $except = [
'locale_lc'
];
Test
<?php
$cookie = ['locale_lc' => 'fr'] ;
$this->actingAs($user)
->withSession(['user' => $user, 'profile' => $profile])
->makeRequest('GET', '/profile', [], $cookie) ;
Middleware
<?php
public function handle($request, Closure $next){
if($request->cookie('locale_lc')){...}
}

Zend 2 and auth configuration routing

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.