Laravel - Rate limiter not bound to user - laravel

I've encountered a pretty weird problem if I say so, a user of an API is reporting that the rate limit of the API is exceeded, when checking this, it seems that the rate-limit is not bound to a specific user, but rather to all users at once.
So when user 1 does an request, the rate-limit for other users will get lowered too.
I've tested this using Postman whilst using two separate Bearer tokens (generated for two unique users)
Does anyone have an idea?

Laravel’s built in throttle middleware bases the limitation on the user’s ip address.
see https://github.com/illuminate/routing/blob/master/Middleware/ThrottleRequests.php
protected function resolveRequestSignature($request)
{
if ($user = $request->user()) {
return sha1($user->getAuthIdentifier());
}
if ($route = $request->route()) {
return sha1($route->getDomain().'|'.$request->ip());
}
throw new RuntimeException('Unable to generate the request signature. Route unavailable.');
}
This would explain why you are seeing the limitations you are in your local test. Depending on your end user's use case, it likely explains their issues as well. Are one or more person testing it with different usernames but from the same physical location?
If you think about what the goals of a throttle like this are, this makes good sense. The use case of the throttle starting over when the same end user start using a new username to try to log in only really makes sense in a testing scenario, and is problematic in production environments.
If you do want to alter this behavior, you could make a new custom middleware in the folder app/Http/Middleware which extends the base Illuminate\Routing\Middleware\ThrottleRequests.
Within your custom middleware, you could then override the method resolveRequestSignature() and change it from ip to something else.
You don’t have an authenticated user to work with, so your choices are limited.
If you have users passing in an api key header, that is a reasonable option. Depending on how you have that set up, it would look something like this:
<?php
namespace App\Http\Middleware;
use Illuminate\Routing\Middleware\ThrottleRequests;
/**
* Class ApiKeyBasedThrottleRequests
*
*
*
* #package App\Http\Middleware
*/
class SessionBasedThrottleRequests extends ThrottleRequests
{
/**
* Override the default, which returns as signature
* sha1($route->getDomain().'|'.$request->ip());
*
* Resolve request signature.
*
* #param \Illuminate\Http\Request $request
* #return string
*
* #throws \RuntimeException
*/
protected function resolveRequestSignature($request)
{
if ($user = $request->user()) {
return sha1($user->getAuthIdentifier());
}
if ($route = $request->route()) {
return sha1($route->getDomain().'|'. $request->header('api-key'));
}
throw new RuntimeException('Unable to generate the request signature. Route unavailable.');
}
}
You would then need to update the reference to the middleware in the file app/Http/Kernel in the array $routeMiddleware. Change the value assigned to the key throttle to point to your new custom middleware, App\Http\Middleware;\ApiKeyBasedThrottleRequests::class instead if \Illuminate\Routing\Middleware\ThrottleRequests::class
If you don't have an api key to work with you could think also about session id, but if a single user is trying different usernames from the same browser session, you will still be having the same problem. In that scenario, you could advise the user to restart their browser and try again. However, this basically creates a workaround to the intent of the throttle.
Think carefully and tread lightly with how you approach this. While it makes sense to make things flexible so you don’t block users, keep in mind one of the intents of the throttle is to stop potential malicious users from brute force attacks to discover working username/password combinations. Anything you do here could give them a potential workaround.

Related

Problem with logging in (Laravel Jetstream)

I have the following problem: Always when I try to log in, on the first attempt the page is just reloading without performing any action like showing up a message for wrong credentials or something like this. On the second attempt everything works fine. Any ideas?
Laravel version: 8
After many, many researches for this kind of problem I think I found my specific solution. I've actually built a route long time ago for logging out which had the following code:
Auth::logout();
return redirect()->route('site.home');
I have no idea why I've created and used this route at all. According to jetstream's core code and my understandings that's not how you fully logout a user and destroy session. But here's how:
/**
* Destroy an authenticated session.
*
* #param \Illuminate\Http\Request $request
* #return \Laravel\Fortify\Contracts\LogoutResponse
*/
public function destroy(Request $request): LogoutResponse
{
$this->guard->logout();
$request->session()->invalidate();
$request->session()->regenerateToken();
return app(LogoutResponse::class);
}
I've tested it right now and everything works fine.

In Laravel, how can I clear my database after my test class is done running?

I have a really large database that I want to introduce testing into and I have been experimenting on ways to approach it.
If I were to run the full migrate:fresh it would take about a minute to create the entire schema. To seed this database it would likely take >5 minutes. My thought was to preload a DB schema into a test database ahead of time, before the tests even start running. Then I was thinking I would seed portions of data as I need it.
This project has multiple domains and it is a DDD design. I'm currently thinking that each domain will have at least one testcase that extends laravels BaseTestcase.
The problem I'm having is that I am not able to clear the data from the database. Here's an example of what a testcase might look like for one of my domains:
use CreatesApplication;
use RefreshDatabase;
use ActingAsUser;
use HasTestHelpers;
/**
* Refresh a conventional test database.
*
* #return void
*/
protected function refreshTestDatabase()
{
if (! RefreshDatabaseState::$migrated) {
$this->artisan('db:seed --class=CourseTestSeeder');
$this->app[Kernel::class]->setArtisan(null);
RefreshDatabaseState::$migrated = true;
}
$this->beginDatabaseTransaction();
}
public static function tearDownAfterClass(): void
{
CourseTestSeeder::clear();
}
This particular domains depends on Course data. I put together a CourseTestSeeder that will seed all the data needed for this domain. This part works great. The problem is that when I try to do a tearDownAfterClass I receive the following error:
Exception in tearDownAfterClass
Target class [db] does not exist.
at vendor/phpunit/phpunit/phpunit:98
94▕ unset($options);
95▕
96▕ require PHPUNIT_COMPOSER_INSTALL;
97▕
➜ 98▕ PHPUnit\TextUI\Command::main();
99▕
How can I clear the db after my tests run? Is there a better way to do this?
If this is not a good approach, I also welcome feedback on that.

Maintaining the session between scenarios in a single feature file in behat and mink

I have done a lot of R&D on this but I am unable to find a solution.
I need to maintain a login session between different scenarios in a single feature file.
I have made a function I am logged in and I have written in background. so at the start of every scenario the login happens. but what I want is to maintain a single login session across the scenarios.
Can anyone suggest?
example Code is:
Feature: To test the output
Background:
Given I am logged in
#javascript
Scenario: To test the positive input
When I fill in "test" with "aab"
And I press "add"
Then I should see "welcome"
#javascript
Scenario:To test the negative inputs
When I fill in "test" with "##$#!!111"
And I press "add"
Then I should see "Sorry,invalid input please try again"
Now if another person reviews my code he would come to know about the positive and negative test cases. but every time the scenarios are reloaded, what if I have 50 scenarios in a feature. for bigger projects. it doesn't look good at every scenario I log in and I waste extra 15 minutes in total. what I want is after every scenario in a single feature file, the test continues with the same login session.
It can't be done. Behat scenarios are independent on purpose. Otherwise, you would risk state leaking from one scenario to another.
You're not approaching the problem from the right direction. Sacrificing scenario separation for speed improvements will hurt you in the long run.
Assuming that logging in is tested as one of the features, in other scenario where logging in is required, you don't have to be using the actual login form. Think of doing it programaticaly.
Also, you seem to be using Behat for functional testing, while it's build for verifying business expectations. You could consider using Mink directly, which would give you more power.
It can be done! I've just found a solution - you need to create an AbstractWebDriver class that maintains a static instance of the webDriver.
FeatureContext
<?php
use Behat\Behat\Tester\Exception\PendingException;
use Behat\Behat\Context\Context;
use Behat\Behat\Context\SnippetAcceptingContext;
use Behat\Gherkin\Node\PyStringNode;
use Behat\Gherkin\Node\TableNode;
use Behat\Behat\Hook\Scope\BeforeScenarioScope;
use Behat\Behat\Hook\Scope\AfterScenarioScope;
use Facebook\WebDriver\Remote\DesiredCapabilities;
use Facebook\WebDriver\Remote\RemoteWebDriver;
use Facebook\WebDriver\WebDriverBy;
/**
* Defines application features from the specific context.
*/
class FeatureContext extends AbstractWebDriver
{
/**
* Initializes context.
*
* Every scenario gets its own context instance.
* You can also pass arbitrary arguments to the
* context constructor through behat.yml.
*/
public function __construct()
{
$capabilities = DesiredCapabilities::safari();
if(!AbstractWebDriver::$webDriver) {
AbstractWebDriver::$webDriver = RemoteWebDriver::create('http://localhost:4444/wd/hub', $capabilities);
}
$this->baseUrl = "http://test.test.com";
}
}
AbstractWebDriver
<?php
use Behat\Behat\Tester\Exception\PendingException;
use Behat\Behat\Context\Context;
use Behat\Behat\Context\SnippetAcceptingContext;
use Behat\Gherkin\Node\PyStringNode;
use Behat\Gherkin\Node\TableNode;
use Behat\Behat\Hook\Scope\BeforeScenarioScope;
use Behat\Behat\Hook\Scope\AfterScenarioScope;
use Facebook\WebDriver\Remote\DesiredCapabilities;
use Facebook\WebDriver\Remote\RemoteWebDriver;
/**
* Defines application features from the specific context.
*/
abstract class AbstractWebDriver extends \PHPUnit\Framework\TestCase implements Context, SnippetAcceptingContext
{
/**
* #var \RemoteWebDriver
*/
protected static $webDriver;
protected $baseUrl;
protected function getDriver()
{
if($this->webDriver==Null)
echo "----------------- Instatiate New Driver -----------------";
$capabilities = DesiredCapabilities::safari();
self::$webDriver = RemoteWebDriver::create('http://localhost:4444/wd/hub', $capabilities);
echo "----------------- Return Current Driver -----------------";
}
}
For one feature file, I can now run multiple scenarios on one instance of a webDriver!
What is the behaviour of this if one of the Scenarios fails ?
"Normally" (if each Scenario has his own session), every Scenario will be executed, even one is falling.
But now, it will stop every Scenario after the one falling ?
Even if you don't have many Scenarios, it can be bothersome to have to correct your Scenario and to relaunch all yours Scenarios... After some time, you will have many Scenarios you will surely execute automatically, imagine if 1 test fails, and all others after are blocked. You will have to re-execute all to verify if those not executed are not failing!
Everything can be done in Php, you just need to think (more or less according to the problem). But the biggest question is "What the consequences ?".
If the developers choose to not do it, it must be a reason. So be aware of what will appends after.

What for is the direct_front_name tag in Magento

Some modules have a <request><direct_front_name>...</direct_front_name></request> in their module config, for example xmlconnect and api. What is this tag for?
What I think it is for:
xmlconnect and api are both used as direct entry points for the site (as opposed normal modules which are reached mostly from within the site). So in combination with the option to use store codes in your store urls you can specify a direct_front_end tag to make the store code not necessary for those modules. This way there is no 404 when calling them without a store code.
(Kind of answered it myself, but couldn't find any information about it online. Might be of use to others. And maybe anyone has something to add.)
You're totally right. And the php DOC clearly tells so :
Mage_Core_Controller_Request_Http::isDirectAccessFrontendName() :
/**
* Check if code declared as direct access frontend name
* this mean what this url can be used without store code
*
* #param string $code
* #return bool
*/
public function isDirectAccessFrontendName($code)

Magento Multiple Authorize.net Gateways

I've seen this question asked with regard to currency type, but what I am asking is how to configure a second Authorize.net account on the same store for a different credit card type. So, we want some credit cards to use the first main Authorize.net gateway, but the others to use the secondary Authorize.net account so that the payments can be routed into two different bank accounts. This is for purposes of reconciliation and is a constraint; can't be modified.
I'm figuring that all I need to do is figure out once the order has been submitted (but before it has been sent via API to Authorize.net) which card type it is, to know which credentials to pass to the API, but I'm unsure as to where to add this code, or the best way in which to add it.
Any insight or advice would be greatly appreciated.
By default, there is no way to accomplish this, so you'll need to use some custom code. Specifically, override the Authnet payment class Mage_Paygate_Model_Authorizenet:
class MyNamespace_MyModule_Model_Authorizenet extends Mage_Paygate_Model_Authorizenet {
/**
* Prepare request to gateway
*
* #link http://www.authorize.net/support/AIM_guide.pdf
* #param Mage_Sales_Model_Document $order
* #return unknown
*/
protected function _buildRequest(Varien_Object $payment)
//see below
}
}
In that function, on line 277 for me, the following code is executed to set the Authnet account:
$request->setXLogin($this->getConfigData('login'))
->setXTranKey($this->getConfigData('trans_key'))
->setXType($payment->getAnetTransType())
->setXMethod($payment->getAnetTransMethod());
Instead, you want something along these lines:
if(whatever cc type) {
// set alternate gateway
} else {
// set default gateway
}
To accomplish this, you'll also want to create new options in the backend to hold the credentials in an encrypted form. Hope that helps!
Thanks,
Joe

Resources