Change Faker Locale in Laravel 5.2 - php

Is there a way to specify the Faker locale in the database/factories/ModelFactory.php file ? Here is my non functioning attempt at doing so >,<
$factory->define(App\Flyer::class, function (Faker\Generator $faker) {
// What is the correct way of doing this?
$faker->locale('en_GB');
return [
'zip' => $faker->postcode,
'state' => $faker->state,
];
});
Thanks for reading!

Faker locale can be configured in the config/app.php configuration file. Just add the key faker_locale.
e.g.: 'faker_locale' => 'fr_FR',
See also my PR to document that previously undocumented feature: https://github.com/laravel/laravel/pull/4161

THIS ANSWER IS ONLY VALID FOR LARAVEL <=5.1 OR WHEN YOU WANT TO USE MANY DIFFERENT LOCALES see this answer for a solution in Laravel >=5.2.
To use a locale with Faker, the generator needs creating with the locale.
$faker = Faker\Factory::create('fr_FR'); // create a French faker
From the faker documentation:
If no localized provider is found, the factory fallbacks to the default locale (en_EN).
Laravel by default, binds the creation of a faker instance in the EloquentServiceProvider. The exact code used to bind is:
// FakerFactory is aliased to Faker\Factory
$this->app->singleton(FakerGenerator::class, function () {
return FakerFactory::create();
});
It would appear that Laravel has no way to modify the locale of the faker instance it passes into the factory closures as it does not pass in any arguments to Faker.
As such you would be better served by using your own instance of Faker in the factory method.
$localisedFaker = Faker\Factory::create("fr_FR");
$factory->define(App\Flyer::class, function (Faker\Generator $faker) {
// Now use the localisedFaker instead of the Faker\Generator
// passed in to the closure.
return [
'zip' => $localisedFaker->postcode,
'state' => $localisedFaker->state,
];
});

I prefer to use it:
$fakerBR = Faker\Factory::create('pt_BR');
$factory->define(App\Flyer::class, function (Faker\Generator $faker) use (fakerBR) {
return [
'name' => $fakerBR->name,
'cpf' => $fakerBR->cpf,
'zip' => $faker->postcode,
'state' => $faker->state,
];
});

Late in the party, but after some research I've found this in faker documentation:
[...] since Faker starts with the last provider, you can easily override existing formatters: just add a provider containing methods named after the formatters you want to override.
That means that you can easily add your own providers to a Faker\Generator instance.
So you can do something like this
$faker->addProvider(new Faker\Provider\pt_BR\Person($faker));
Just before return [] and then use specific providers, like (in this case) $faker->cpf;
Tested on Laravel 5.3
More info on Faker documentation

#IvanAugustoDB, there is a another form of doing that. When Laravel initalize faker, it can be constructed on another locale, just create a Service Provider and put the following snippet inside it.
use Faker\Generator as FakerGenerator;
use Faker\Factory as FakerFactory;
$this->app->singleton(FakerGenerator::class, function () {
return FakerFactory::create('pt_BR');
});

$factory->define(App\User::class, function (Faker\Generator $faker) {
$faker->addProvider(new Faker\Provider\ng_NG\Person($faker));
$faker->addProvider(new Faker\Provider\ng_NG\PhoneNumber($faker));
...
in the above code, "ng_NG" is for Nigeria and can be replaced with any other locale.
To my knowledge, you would have to specify Person, PhoneNumber and others depending on what you have in your vendor\fzaninotto\faker\src\Faker\Provider folder. However if the provider you intend using isn't available, then it will resolve back to using "en".
This works like charm for me, and it should work for you too.

This answer is valid just for Laravel 5.4 and greater:
Since this pull, you can just use 'faker_locale' as a variable in your app config file. It just works really good.

this is the link for all providers that used in faker
for arabic lang example
use Faker\Factory as Faker; ### in the head off class
$faker = Faker::create();
$faker_ar = Faker::create('ar_SA');
for ($i = 0; $i < 10; $i++) {
DB::table('categories')->insert([
'name' => $faker->name,
'name_ar' => $faker_ar->name,
'created_at' => now(),
'updated_at' => now(),
]);
}

If you are using multiple languages for the same table and can't use local
you can use: shuffleString
'name'=>$faker->shuffleString('abddefhig')
'name_ar'=>$faker->shuffleString('البتثجحخدزسش')

Related

How to allow to use the master password in Laravel 8 by overriding Auth structure?

I've got a website written in pure PHP and now I'm learning Laravel, so I'm remaking this website again to learn the framework. I have used built-in Auth Fasade to make authentication. I would like to understand, what's going on inside, so I decided to learn more by customization. Now I try to make a master password, which would allow direct access to every single account (as it was done in the past).
Unfortunately, I can't find any help, how to do that. When I was looking for similar issues I found only workaround solutions like login by admin and then switching to another account or solution for an older version of Laravel etc.
I started studying the Auth structure by myself, but I lost and I can't even find a place where the password is checked. I also found the very expanded solution on GitHub, so I tried following it step by step, but I failed to make my own, shorter implementation of this. In my old website I needed only one row of code for making a master password, but in Laravel is a huge mountain of code with no change for me to climb on it.
As far I was trying for example changing all places with hasher->check part like here:
protected function validateCurrentPassword($attribute, $value, $parameters)
{
$auth = $this->container->make('auth');
$hasher = $this->container->make('hash');
$guard = $auth->guard(Arr::first($parameters));
if ($guard->guest()) {
return false;
}
return $hasher->check($value, $guard->user()->getAuthPassword());
}
for
return ($hasher->check($value, $guard->user()->getAuthPassword()) || $hasher->check($value, 'myHashedMasterPasswordString'));
in ValidatesAttributes, DatabaseUserProvider, EloquentUserProvider and DatabaseTokenRepository. But it didn't work. I was following also all instances of the getAuthPassword() code looking for more clues.
My other solution was to place somewhere a code like this:
if(Hash::check('myHashedMasterPasswordString',$given_password))
Auth::login($user);
But I can't find a good place for that in middlewares, providers, or controllers.
I already learned some Auth features, for example, I succeed in changing email authentication for using user login, but I can't figure out, how the passwords are working here. Could you help me with the part that I'm missing? I would appreciate it if someone could explain to me which parts of code should I change and why (if it's not so obvious).
I would like to follow code execution line by line, file by file, so maybe I would find a solution by myself, but I feel like I'm jumping everywhere without any idea, how this all is connected with each other.
First of all, before answering the question, I must say that I read the comments following your question and I got surprised that the test you made returning true in validateCredentials() method in EloquentUserProvider and DatabaseUserProvider classes had failed.
I tried it and it worked as expected (at least in Laravel 8). You just need a an existing user (email) and you will pass the login with any non-empty password you submit.
Which of both classes are you really using (because you don't need to edit both)? It depends of the driver configuration in your auth.php configuration file.
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Models\User::class,
],
// 'users' => [
// 'driver' => 'database',
// 'table' => 'users',
// ],
],
As you already thought, you can simply add an "or" to the validation in the validateCredentials() method, comparing the $credentials['password'] to your custom master password.
Having said that, and confirming that's the place where you'd have to add your master password validation, I think the best (at least my recommended) way to accomplish your goal is that you track the classes/methods, starting from the official documentation, which recommends you to execute the login through the Auth facade:
use Illuminate\Support\Facades\Auth;
class YourController extends Controller
{
public function authenticate(Request $request)
{
//
if (Auth::attempt($credentials)) {
//
}
//
}
}
You would start by creating your own controller (or modifying an existing one), and creating your own Auth class, extending from the facade (which uses the __callStatic method to handle calls dynamically):
use YourNamespace\YourAuth;
class YourController extends Controller
{
//
public function authenticate(Request $request)
{
//
if (YourAuth::attempt($credentials)) {
//
}
//
}
}
//
* #method static \Illuminate\Contracts\Auth\Guard|\Illuminate\Contracts\Auth\StatefulGuard guard(string|null $name = null)
//
class YourAuth extends Illuminate\Support\Facades\Facade
{
//
}
And use the same logic, overriding all the related methods in the stack trace until you get to use the validateCredentials() method, which in the end will also be overrided in your own CustomEloquentUserProvider class which will be extending fron the original EloquentUserProvider.
This way, you will have accomplished your goal, and kept a correct override of the whole process, being able to update your laravel installation without the risk of loosing your work. Worst case scenario? You'll have to fix any of your overriding methods in case that any of them has drastically changed in the original classes (which has a ver low chance to happen).
Tips
When making the full overriding, maybe you'll prefer to add some significant changes, like evading the interfaces and going straight for the classes and methods you really need. For example: Illuminate/Auth/SessionGuard::validate.
You would also wish to save your master password in an environment variable in your .env file. For example:
// .env
MASTER_PASSWORD=abcdefgh
and then call it with the env() helper:
if ($credentials['password'] === env('MASTER_PASSWORD')) {
//
}
Nice journey!
A more complete solution would be the define a custom guard and use that instead of trying to create your own custom auth mechanism.
Firstly, define a new guard within config/auth.php:
'guards' => [
'master' => [
'driver' => 'session',
'provider' => 'users',
]
],
Note: It uses the exact same setup as the default web guard.
Secondly, create a new guard located at App\Guards\MasterPasswordGuard:
<?php
namespace App\Guards;
use Illuminate\Auth\SessionGuard;
use Illuminate\Support\Facades\Auth;
class MasterPasswordGuard extends SessionGuard
{
public function attempt(array $credentials = [], $remember = false): bool
{
if ($credentials['password'] === 'master pass') {
return true;
} else {
return Auth::guard('web')->attempt($credentials, $remember);
}
}
}
Note:
You can replace 'master pass' with an env/config variable or simply hardcode it. In this case I'm only checking for a specific password. You might want to pair that with an email check too
If the master pass isn't matched it falls back to the default guard which checks the db
Thirdly, register this new guard in the boot method of AuthServiceProvider:
Auth::extend('master', function ($app, $name, array $config) {
return new MasterPasswordGuard(
$name,
Auth::createUserProvider($config['provider']),
$app->make('session.store'),
$app->request
);
});
Fourthly, in your controller or wherever you wish to verify the credentials, use:
Auth::guard('master')->attempt([
'email' => 'email',
'password' => 'pass'
]);
Example
Register the route:
Route::get('test', [LoginController::class, 'login']);
Create your controller:
namespace App\Http\Controllers;
use Illuminate\Support\Facades\Auth;
class LoginController
{
public function login()
{
dd(
Auth::guard('master')->attempt([
'email' => 'demo#demo.com',
'password' => 'master pass'
]),
Auth::guard('master')->attempt([
'email' => 'demo#demo.com',
'password' => 'non master'
]),
);
}
}
and if you hit this endpoint, you'll see:
Where true is where the master password was used and false is where it tried searching for a user.
Final Thoughts
From a security standpoint you're opening yourself up to another attack vector and one which is extremely detrimental to the security of your system and the privacy of your users' data. It would be wise to reconsider.
This validation of credentials should ideally be separated from your controller and moved to a Request class. It'll help keep your codebase more clean and maintainable.
Instead of trying to roll your own, you could as well as use a library, which does just that:laravel-impersonate (it's better tested already). This also comes with Blade directives; just make sure to configure it properly, because by default anybody can impersonate anybody else.
There even is (or was) rudimentary support available with: Auth::loginAsId().
Here is a possible solution.
To use a master password, you can use the loginUsingId function
Search the user by username, then check if the password matches the master password, and if so, log in with the user ID that it found
public function loginUser($parameters)
{
$myMasterHashPassword = "abcde";
$username = $parameters->username;
$password = $parameters->password;
$user = User::where('username', $username)->first();
if (!$user) {
return response("Username not found", 404);
}
if (Hash::check($myMasterHashPassword, $password)) {
Auth::loginUsingId($user->id);
}
}

How to over ride a Laravel class with my own?

So I am working with Laravel 5.2 and I trying to shift to SQS as my queue serice.
There was a bug in 5.2 in QueueSqs.php which was fixed here -> https://github.com/illuminate/queue/blob/5.6/SqsQueue.php in 5.6
Now I am not sure I can upgrade to 5.6 yet, because a lot of things are working with 5.2 and I don't want to break anything.
But I am sure I can somehow use this class in my code from 5.6 and tell Laravel to use it somehow. But I don't know how to.
I haven't checked if this worked back in Laravel 5 or in class extends, but in newer versions Laravel generally tries to resolve the class from its container and by binding the original FQNS of the class to your your custom SqsQueue, it instead returns yours everytime the original is used:
class AppServiceProvider extend ServiceProvider
{
public function register()
{
$this->app->bind(\Illuminate\Queue\SqsQueue::class, function ($app) {
return new CustomSqsQueue();
});
}
}
Well, actually, instead of overriding the original class and re-binding it for Laravel to use you could also add your own custom queue connector and use this instead of Laravels native SQS queue connector:
To add a new connector for the queue to use, open up a service provider and add the following code to the boot
public function register()
{
$manager = $this->app['queue'];
$manager->addConnector('sqs-custom', function() {
return new CustomSqsQueue;
});
}
Then, in the queue.php config, add a new connection based on the original SQS connection, but with the driver name you chose in the first parameter or addConnector in the service provider. The key of the array ist the name of the connection you use to define your queues:
'sqs-custom' => [
'driver' => 'sqs-custom',
'key' => 'your-public-key',
'secret' => 'your-secret-key',
'prefix' => 'https://sqs.us-east-1.amazonaws.com/your-account-id',
'queue' => 'your-queue-name',
'region' => 'us-east-1',
],
Again, this is untested but should be able to work in Laravel 5.2.

Laravel model factories using same seed for testing purposes

I am trying to set up a default seed for Faker in Laravel which is normally achieved in this way (not in Laravel):
<?php
$faker = Faker\Factory::create();
$faker->seed(1234);
according to Faker's GitHub page.
I am trying to do this so that can I get the same data generated each time so that I can write some unit tests but I have no idea how to do that in Laravel. I've checked Laravel's documentation and tried googling the issue but I found nothing.
Here's how to do apply the seed to Faker in Laravel 5.
Inside your app/database/factories directory, create a new file. I called mine SeedFactory.php.
<?php
$factory->faker->seed('1');
That's it!
Now you have consistent random data for your unit testing!
NB: If you only have one or two factories, you could append that line to an existing factory file.
Here's why it works.
When Laravel processes all the files in the app/database/factories directory, it executes them straightaway. The $factory object passed around is an instance of Illuminate\Database\Eloquent\Factory.php, which keeps with it it's own internal Faker\Generator instance.
Also, you won't need to worry about the naming of the file or execution order, because this will get fired before any of the factory callbacks (assuming you did it as instructed in the Laravel docs).
it is easy. Just define a factory. Let's have a look at the default factory shipped
with laravel 5.5
File: database/factories/ModelFacotry.php
<?php
/*
|--------------------------------------------------------------------------
| Model Factories
|--------------------------------------------------------------------------
|
| Here you may define all of your model factories. Model factories give
| you a convenient way to create models for testing and seeding your
| database. Just tell the factory how a default model should look.
|
*/
/** #var \Illuminate\Database\Eloquent\Factory $factory */
$factory->define(App\User::class, function (Faker\Generator $faker) {
static $password;
// Add this line to original factory shipped with laravel.
$faker->seed(123);
return [
'name' => $faker->name,
'email' => $faker->unique()->safeEmail,
'password' => $password ?: $password = bcrypt('secret'),
'remember_token' => str_random(10),
];
});
Then use tinker to test it:
yudu#YududeMacBook-Pro ~/demo> php artisan tinker
Psy Shell v0.8.1 (PHP 7.1.8 — cli) by Justin Hileman
>>> $user = factory(App\User::class)->make()
=> App\User {#880
name: "Jessy Doyle",
email: "jalen86#example.net",
}
>>> $user = factory(App\User::class)->make()
=> App\User {#882
name: "Jessy Doyle",
email: "lbode#example.org",
}
Laravel Docs:
how to define and use factory
Seeding

zf3 change locale depending on user selection

I have Zend Framework 3 Application with working translator using po files.
I have configured it like this in my \config\global.php file:
'translator' => [
'locale' => 'en_US',
'translation_file_patterns' => [
[
'type' => 'gettext',
'base_dir' => getcwd() . '/data/language/',
'pattern' => '/%s/general.mo',
],
],
],
When i change the value of the "locale" it works ok and finds the proper .po file.
I need to be able to set the locale depending on a user profile's value saved in the database.
I have checked the documentation from here http://zendframework.github.io/zend-i18n/translation/ and the tutorial from here https://docs.zendframework.com/tutorials/i18n/ but they just mention the setLocale() method with no explanation or example. There is similar thread here Zend framework 2 : How to set locale globaly? but it's for ZF2 and it doesn't provide working solution just some suggestions.
To summarize my question - how and where should i use the setLocale() method so it would be effective in the whole application and $this->translate($message) in all view files will use the new locale instead the default one used in the configuration file?
You just need to set the PHP locale. To do so, use \Locale::setDefault('en-GB');.
Have a look at SlmLocale, this specific file is where it's done.
While that was the easiest way, you could also use the setLocale function on the MvcTranslator I guess. For that, you would need to override the existing factory with your own factory, therefore decorating the original one.
If you look at the ConfigProvider file in zend-mvc-i18n, you can see that aliases and factories are used here to create the MVC translator. Then you can see how the factory in question works, it basically creates a decorate translator, as stated in the doc.
By default, the service manager always provide the same instance (shared service), just like a singleton.
What we will therefore do is override this configuration (ie. make sure your own module is after the Zend\Mvc\I18n in modules.config.php). Then, in the module configuration, we can provide our own translator.
Our translator basically consist of the translator from the documentation, on which the setLocale is called. In order to do so, we can use a delegator.
return [
'factories' => [
TranslatorInterface::class => TranslatorServiceFactory::class,
],
'delegators' => [
TranslatorInterface::class => [
\Application\Factory\TranslatorFactory::class,
],
],
];
And then the TranslatorFactory:
use Interop\Container\ContainerInterface;
use Zend\ServiceManager\Factory\DelegatorFactoryInterface;
class TranslatorFactory implements DelegatorFactoryInterface
{
public function __invoke(ContainerInterface $container, $name, callable $callback, array $options = null)
{
$translator = call_user_func($callback);
$translator->setLocale('en-GB');
return $translator;
}
}
That would be one way to do it (you get the container in that factory, so you could get some user data probably).
Another solution is to use the event system, and only declare the locale in the event listener where you retrieve your user details.

PHPUnit Laravel Hash not available

I have a Unit test in Laravel for testing an API call that looks like this, however I am getting the following runtime error when running it:
RuntimeException: A facade root has not been set.
I'm creating a user in the setup method, with the intent to delete it again in the tearDown() method, then run my auth test.
Firstly, is there a better way of doing what I want? For example Mocking a user without touching the database? And secondly, how do I set a 'facade root' or what does that error mean exactly? I've tried not bothering to hash that particular field for the purposes of creating a Dummy user, but the error then seems to move to the model, where (again) the Hash facade class is used.
Is there any additional steps to setup the environment so these facades can be used in testing?
Thanks in advance.
use Illuminate\Support\Facades\Hash;
/*
* Make sure the structure of the API call is sound.
*/
public function testAuthenticateFailed()
{
$this->json('POST', $this->endpoint,
[ 'email' => 'test#test.com',
'password' => 'password',
])
->seeJsonStructure([
'token'
]);
}
//create a user if they don't already exist.
public function setup()
{
$user = User::create([
'company_id' => 9999,
'name'=>'testUser',
'email' => 'test#test.com',
'password' => 'password',
'hashed_email' => Hash:make('test#test.com'),
]);
}
Try to use this instead:
\Hash::make('test#test.com'),
It's a good idea to use bcrypt() global helper instead of Hash::make()
Also, add this to setUp() method:
parent::setUp();
You could use the DatabaseMigrations or DatabaseTransactions trait that comes with Laravel so you don't have to delete the User manually.
You could add a Mutator to your User class, which will automatically hash the password when a User is created.
// https://laravel.com/docs/5.3/eloquent-mutators
public function setPasswordAttribute($value) {
$this->attributes['password'] = bcrypt($value);
}

Categories