Laravel skipping "required" file validation - php

i'm having a problem with laravel 8, i really don't know what i'm doing wrong but the files are skipping the required rule in the validation, i'm sending the form with an array of names and files, and executing the validation in the controller:
public function store(StoreExamenRequest $examenRequest, StoreResultadoRequest $resultadoRequest)
{
$validacionExamen = $examenRequest->validated();
$validacionResultado = $resultadoRequest-validated();
the part that is not validating the file is $resultadoRequest-validated(); This is the content of StoreResultadoRequest
class StoreResultadoRequest extends FormRequest {
public function authorize()
{
return true;
}
public function rules()
{
$this->redirect => url()->previous();
return [
'NombreResultado.*' => 'required|string',
'ArchivoResultado.*' => 'required|file|mimes:pdf|max:1024',
];
}
public function messages()
{
return [
'NombreResultado.required' => __('NombreResultado.required'),
'ArchivoResultado.required' => __('ArchivoResultado.required'),
'ArchivoResultado.file' => __('ArchivoResultado.file'),
'ArchivoResultado.mimes' => __('ArchivoResultado.mimes'),
'ArchivoResultado.max' => __('ArchivoResultado.max')
];
}
}
NombreResultado.required required rule is validating ok, the problem is with ArchivoResultado.required it is validating all the rules except for the required rule. I’ve tried deleting all the other rules and leaving only that rule but it's not working. This is a dd of the $validacionResultado in the controller when i submit the form without the file:
array:1 [
"NombreResultado" => array:1 [
0 => "Name of the file"
]
]
This is a dd with the file attached:
array:2 [
"NombreResultado" => array:1 [
0 => "Name of the file"
]
"ArchivoResultado" => array:1 [
0 => Illuminate\Http\UploadedFile {#1328
-test: false
-originalName: "factura_00000525.pdf"
-mimeType: "application/pdf"
-error: 0
#hashName: null
path: "/private/var/tmp"
filename: "phpoVUvWV"
basename: "phpoVUvWV"
pathname: "/private/var/tmp/phpoVUvWV"
extension: ""
realPath: "/private/var/tmp/phpoVUvWV"
aTime: 2021-03-13 04:53:56
mTime: 2021-03-13 04:53:56
cTime: 2021-03-13 04:53:56
inode: 80503611
size: 136213
perms: 0100600
owner: 70
group: 0
type: "file"
writable: true
readable: true
executable: false
file: true
dir: false
link: false
}
]
]
i'm not attaching a dd of $resultadoRequest because it's too long. Can you please point me in the right direction?

You want to check that the array of files is present and not empty, then check each file in the array. You already have the part that checks each file in the array, so add the check of array itself.
return [
'NombreResultado.*' => 'required|string',
'ArchivoResultado' => 'required|array',
'ArchivoResultado.*' => 'required|file|mimes:pdf|max:1024',
];

Related

Symfony unit testing with loginUser(), login not working (returning 302 to login page)

I'm building a test for a Symfony 5.4 application.
I have created a test like this:
public function testCreateProduct() {
$client = static::createClient();
/** #var User $mainAdmin */
$mainAdmin = static::getContainer()->get(UserRepository::class)->find(1);
//$client->catchExceptions(false);
$client->loginUser($mainAdmin);
$crawler = $client->request('GET', '/en/product/new');
$this->assertResponseIsSuccessful();
$this->assertSelectorTextContains('span.username', $mainAdmin->getUsername());
}
But the login is not working, I get a 302 redirect to the login page, and if I set catchExceptions(false) I get an AccessDeniedException.
How can I debug this?
Edit:
I tried to change the patch to a public route, then I did a dd($this->getUser(), $request)
getUser() is null, but the request session contains the user:
#session: Symfony\Component\HttpFoundation\Session\Session {#16616
#storage: Symfony\Component\HttpFoundation\Session\Storage\MockFileSessionStorage {#16631
-savePath: "C:\www\project\var\cache\test/sessions"
#id: "a9d00704e1a0211d06ebddadfaabbf0188e9d65d94faac05afbdc63bb9fb7caa"
#name: "MOCKSESSID"
#started: true
#closed: false
#data: array:3 [
"_sf2_attributes" => &1 array:1 [
"_security_main" => "O:52:"Symfony\Bundle\FrameworkBundle\Test\TestBrowserToken":2:{i:0;s:4:"main";i:1;a:5:{i:0;C:15:"App\Entity\User":118:{a:5:{i:0;i:2;i:1;s:5:"admin";i:2;s:60:"$2y$13$abcdefghi......";i:3;i:1;i:4;b:0;}}i:1;b:1;i:2;N;i:3;a:0:{}i:4;a:9:{i:0;s:9:"ROLE_USER";i:1;s:10:"ROLE_ADMIN";i:2;[...]}}}"
]
"_sf2_meta" => &2 array:3 [
"u" => 1645718565
"c" => 1645718565
"l" => 0
]
"_symfony_flashes" => &3 []
]
I see a difference between
$request->getSession()->getBag('attributes')
done in dev (working) vs in test: in dev I have _security_secured_area, while in test I have _security_main. May this be the reason?
Solved.
Since I was using a custom firewall name in security.yaml, in my case secured_area, I had to pass that as the second parameter of loginUser():
$client->loginUser($mainAdmin, 'secured_area');

Laravel 8 file upload validation fails with any rule

I want to validate a file upload but with literally any validation rule, I get "The (name of input) failed to upload." I've seen this issue in a few places but none of the solutions worked for me.
I'm using Laravel 8.0, php 8.0.2, and nginx/1.18.0 on ubuntu.
Controller:
public function uploadMedia (Request $request)
{
$request->validate([
'file' => 'required',
'alt' => 'required',
]);
dd('valid');
}
Blade file:
#if($errors->any())
#foreach ($errors->all() as $error)
<p class="alert error">{{ $error }}</p>
#endforeach
#endif
<form method="POST" action="/media" enctype="multipart/form-data">
#csrf
<input type="file" name="file" />
<input type="text" name="alt" placeholder="Write a short description of the image under 160 characters." />
<button type="submit">Upload</button>
</form>
If I get rid of the validation rule for 'file' it works, I get to the dd.
$request->validate([
'file' => '', // works if I do this
'alt' => 'required',
]);
I've seen other people have this issue and I've tried:
Putting another rule (max:10000, image) instead of 'required'
for the file – still get the same error.
Changing the values php.ini to post_max_size = 201M and
upload_max_filesize = 200M (this shouldn't be an issue in the
first place because the image I am trying to upload is a 136kb jpg). Verified with phpinfo();
After changing these values, reloading nginx and php-fpm
Rebooting the VM
Checking that all the double and single quotes are the correct character
Trying to upload other filetypes like png or txt, same error.
Putting 'image' as the first validation rule which worked for someone here
Removing the extra comma at the end of the rules array
Changing the name of the file input to something other than 'file'
Using a different browser (Firefox and Chrome)
Disabling all my browser extensions
Writing the validation like this instead (still get the same error):
$validator = Validator::make($request->all(), [
'file' => 'required',
'alt' => 'required'
]);
if ($validator->fails()) {
return redirect('/media')->withErrors($validator);
}
If I dd($request) before validating:
Illuminate\Http\Request {#44 ▼
#json: null
#convertedFiles: null
#userResolver: Closure($guard = null) {#255 ▶}
#routeResolver: Closure() {#264 ▶}
+attributes: Symfony\Component\HttpFoundation\ParameterBag {#46 ▶}
+request: Symfony\Component\HttpFoundation\ParameterBag {#45 ▼
#parameters: array:2 [▼
"_token" => "RrjAA2YvnSd3EYqg8vAwoWT4y6VenJzGjb5S72SU"
"alt" => "dsfghdf"
]
}
+query: Symfony\Component\HttpFoundation\InputBag {#52 ▶}
+server: Symfony\Component\HttpFoundation\ServerBag {#49 ▶}
+files: Symfony\Component\HttpFoundation\FileBag {#48 ▼
#parameters: array:1 [▼
"file" => Symfony\Component\HttpFoundation\File\UploadedFile {#33 ▼
-test: false
-originalName: "Screen Shot 2021-03-08 at 9.33.19 AM.png"
-mimeType: "application/octet-stream"
-error: 6
path: ""
filename: ""
basename: ""
pathname: ""
extension: ""
realPath: "/var/www/[my domain]/public"
aTime: 1970-01-01 00:00:00
mTime: 1970-01-01 00:00:00
cTime: 1970-01-01 00:00:00
inode: false
size: false
perms: 00
owner: false
group: false
type: false
writable: false
readable: false
executable: false
file: false
dir: false
link: false
}
]
}
+cookies: Symfony\Component\HttpFoundation\InputBag {#47 ▶}
+headers: Symfony\Component\HttpFoundation\HeaderBag {#50 ▶}
#content: null
#languages: null
#charsets: null
#encodings: null
#acceptableContentTypes: null
#pathInfo: "/media"
#requestUri: "/media"
#baseUrl: ""
#basePath: null
#method: "POST"
#format: null
#session: Illuminate\Session\Store {#296 ▶}
#locale: null
#defaultLocale: "en"
-preferredFormat: null
-isHostValid: true
-isForwardedValid: true
-isSafeContentPreferred: null
basePath: ""
format: "html"
}
Error value of 6 means UPLOAD_ERR_NO_TMP_DIR. Ensure that your system has a properly configured upload temp directory by running php -i from command line (or phpinfo(); from a web page) and checking for the upload_tmp_dir key. On a typical Linux system this will be something like /tmp. You can set the value in php.ini if needed. Ensure permissions are correct on the listed folder, such that the web server process is allowed to write to it.
Do not attempt to use one of your publicly-accessible folders as an upload directory (e.g. saving directly to storage/app/public or similar.) Your application code should move the file into storage as described in the documentation; something like this:
public function uploadMedia(Request $request)
{
$request->validate([
'file' => ['required', 'file', 'image'],
'alt' => ['required', 'string'],
]);
$path = $request->file->store('images');
return redirect()->route('whatever')->with('success', "File saved to $path");
}

Laravel Validation – Date Format m/y Not Accepting Specific Value

I've got the following validation rules for basic authentication of a Payment Method (advanced things, like CVD validation, existing card, etc. is handled afterward by Moneris).
$rules = [
"type" => "required|in:visa,mastercard",
"nickname" => "required",
"credit_card_number" => "required|numeric|digits:16",
"expiry" => "required|string|size:5|date_format:m/y|after:today",
"cvd" => "required|numeric|digits:3"
];
The rule expiry is not accepting a specific value, 04/yy, but it is accepting 03/yy and 05/yy; I have no idea why this is happening, but I need it remedied. Has anyone come across this behaviour?
For reference, the result dd($request->input(), $validator->passes(), $validator->errors()); when I pass 04/19 is as follows:
array:6 [▼
"type" => "visa"
"nickname" => "Testing"
"credit_card_number" => "4242424242424242"
"expiry" => "04/19"
"cvd" => "123"
"masked_pan" => "************4242"
]
false
MessageBag {#502 ▼
#messages: array:1 [▼
"expiry" => array:1 [▼
0 => "The expiry does not match the format m/y."
]
]
#format: ":message"
}
When I send 05/19, everything works fine:
array:6 [▼
"type" => "visa"
"nickname" => "Testing"
"credit_card_number" => "4242424242424242"
"expiry" => "05/19"
"cvd" => "123"
"masked_pan" => "************4242"
]
true
MessageBag {#502 ▼
#messages: []
#format: ":message"
}
Looks like it's an issue with how this validation rule works in Laravel 5.4. To fix, I check the date validity of the input prepended with 01/, and if it is valid, merge that into the request, with endOfMonth() to handle after:today validation:
$mergeDate = null;
$rawInput = $request->input("expiry");
try {
$mergeDate = Carbon::createFromFormat("d/m/y", "01/".$request->input("expiry"))->endOfMonth();
} catch(\Exception $ex){}
$request->merge([
"masked_pan" => str_repeat("*", 12).substr($request->input("credit_card_number", ""), -4),
"expiry" => $mergeDate ? $mergeDate->format("d/m/y") : $request->input("expiry")
]);
So now, if I pass 04/22, it will check if 01/04/22 is valid, then convert to end of month 30/04/22, then replace that as the value passed to the validation (which also needs to be updated)
"expiry" => "required|string|size:8|date_format:d/m/y|after:today",
I also have to update and pass $messages to avoid confusion to the user:
$messages = [
"expiry.size" => "The :attribute filed must be 5 characters.",
"expiry.date_format" => "The :attribute field does not match the format m/y"
];
$validator = \Validator::make($request->all(), $rules, $messages);
And finally, replace the value with the raw input if there's an error (so the user doesn't see a value they didn't enter)
if(!$validator->passes()){
$request->merge(["expiry" => $rawInput]);
return back()->withErrors($validator)->withInput();
}
A whole bunch of nonsense, but seems to handle 04/22 and other dates just fine.

How to use on Laravel guard for the main domain and another for the subdomains

I'm building a multi-tenant application where the idea is the admins access through the main domain (http://myapp.app) to the dashboard and the regular users access to another dashboard on their respective subdomains (http://tenant-a.myapp.app).
To achieve this I created a custom guard(admin) that uses the session driver and the admins provider which is a custom provider that uses the eloquent driver and my table admins.
// config/auth.php
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'jwt',
'provider' => 'users',
],
'admin' => [
'driver' => 'session',
'provider' => 'admins',
]
],
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => Monica\Models\User::class,
],
'admins' => [
'driver' => 'eloquent',
'model' => Monica\Models\Admin::class,
],
],
After reading a lot of documentation about how the authentication works I got the admins dashboard working event with the reset password system but the part of the subdomains still present some issues.
The login for the regular users on the tenant subdomain seems to be working since I got my user logged and if I check the remember option this is reflected on the database getting a token.
The most important issue that have is when I try to retrieve the user using the authentication functions (facade or injected) I can't get the user, the method always return me null.
I've tried to specify the guard to the auth object but still not work.
When I use the guard object it has a member user that it suppose to contain the logged user but it is always null and if you are about to ask me why I need the user it's because I need to check the permissions of the user.
My theories are that my session only works with the main domain and not with the subdomians or I need to specify another cookie but honestly I'm just guessing.
I don't even know what part of my code will be useful to post but if you are related with this problem, every light that you can give me is welcome, let me know if you need more information or an specific piece of my code.
Thanks in advance
UPDATE
This is an extract of the UserController.php
use Illuminate\Auth\AuthManager as Auth;
use Illuminate\Contracts\Auth\Access\Gate;
use Monica\Http\Controllers\Controller;
class UsersController extends Controller
{
protected $auth;
protected $gate;
public function __construct(Auth $auth, Gate $gate)
{
$this->middleware('web');
$this->auth = $auth;
$this->gate = $gate;
$this->auth->guard('web');
$this->auth->shouldUse('web');
$u = $this->auth->guard();
dd($u);
}
}
And this is the guard object dumped:
SessionGuard {#311 ▼
#name: "admin"
#lastAttempted: null
#viaRemember: false
#session: Store {#294 ▼
#id: "XIWy7hEJRuX1cL2bBN7pf7DqT54PpbTyYBXPv6He"
#name: "no_named_app_session"
#attributes: array:5 [▼
"_token" => "RrTXOZwj56Nk9OqxkdkLdDztfZb6TeW2knVf5xc7"
"_previous" => array:1 [▼
"url" => "http://monica.app/admin/admins"
]
"_flash" => array:2 [▼
"old" => []
"new" => []
]
"url" => []
"login_admin_59ba36addc2b2f9401580f014c7f58ea4e30989d" => "66f4aab0-6566-11e8-b51d-673dcbafed23"
]
#handler: FileSessionHandler {#295 ▼
#files: Filesystem {#115}
#path: "/home/vagrant/Code/PHP/monica/storage/framework/sessions"
#minutes: "120"
}
#started: true
}
#cookie: CookieJar {#292 ▼
#path: "/"
#domain: null
#secure: false
#sameSite: null
#queued: []
}
#request: Request {#42 ▶}
#events: Dispatcher {#26 ▶}
#loggedOut: false
#recallAttempted: false
#user: Admin {#328 ▶}
#provider: EloquentUserProvider {#308 ▼
#hasher: BcryptHasher {#310 ▶}
#model: "Monica\Models\Admin"
}
}
After reviewed the life cycle of the Laravel Request I found that the user is not available inside of the Auth or Guard objects when the constructor of the Controller classes is being executed
If you try to access to the logged user on the controller constructor
public function __construct(Auth $auth, Gate $gate)
{
$this->auth = $auth;
$this->gate = $gate;
$this->middleware('auth:web');
$this->auth->shouldUse('web');
$user = $this->auth->user() // null
}
But if you access to the user in on the the controller methods, the user is going to be returned
public function index($subdomain)
{
$user = $this->auth->user()->toArray();
}
This is user var dumped
array:6 [▼
"id" => "670732c0-6566-11e8-93c6-41f6face77c8"
"tenant_id" => "66f815e0-6566-11e8-83b2-37a662a96205"
"name" => "user"
"email" => "user#aetech.com"
"created_at" => "2018-06-01 06:38:32"
"updated_at" => "2018-06-01 06:38:32"
]
You can try a couple different approaches to set the guard dynamically.
1. Using auth()->shouldUse('the_guard')
In a middleware or a controller's constructor, pass the name of the guard into the shouldUse method depending on the domain or hostname:
if (request()->getHttpHost() === 'myapp.com') {
auth()->shouldUse('admin');
} else {
auth()->shouldUse('api'); // use the guard for tenants
}
2. Override the default config in config/auth.php, again, in a middleware or controller constructor:
if (request()->getHttpHost() === 'myapp.com') {
config(['auth.defaults.guard' => 'admin');
} else {
config(['auth.defaults.guard' => 'api'); // use the guard for tenants
}
If you're using custom logic to login, be sure that the Authenticable model is set using one of the auth methods: login, once, attempt, etc.
I use the first method, shouldUse, across projects with an abstract base controller class which all other applicable controllers inherit from. Hope this helps.

Laravel 5.5 Geocoder

I wanted to use this package for geocoding in Laravel. I have added it to providers and published the config, but I am getting trouble setting it up to work.
try {
$location = Geocoder::geocode('68.145.37.34')->get();
return $location;
} catch (\Exception $e) {
return $e;
}
This returns empty object.
I have left the config file as is.
return [
'cache-duration' => 9999999,
'providers' => [
Chain::class => [
GoogleMaps::class => [
'en-US',
env('GOOGLE_MAPS_API_KEY'),
],
GeoPlugin::class => [],
],
],
'adapter' => Client::class,
];
And added valid API key to env. Is there something I'm missing?
Geocoder is imported as use Geocoder\Laravel\Facades\Geocoder;
EDIT
In case someone gets to the same problem...this is how you'd get the country from it:
app('geocoder')->geocode('5.43.168.58')->get()->first()->getCountry()->getName();
Really complicated unnecessarily in my opinion, I requested a documentation change on official repo.
did you try using dd() in tinker?? I have been try it...and it work for me..
try this :
dd(app('geocoder')->geocode('68.145.37.34')->get());
response :
Illuminate\Support\Collection {#764
items: array:1 [
0 => Geocoder\Model\Address {#753
-coordinates: Geocoder\Model\Coordinates {#755
-latitude: 51.0823
-longitude: -113.9578
}
-bounds: null
-streetNumber: null
-streetName: null
-subLocality: null
-locality: "Calgary"
-postalCode: null
-adminLevels: Geocoder\Model\AdminLevelCollection {#767
-adminLevels: array:1 [
1 => Geocoder\Model\AdminLevel {#768
-level: 1
-name: "Alberta"
-code: "AB"
}
]
}
-country: Geocoder\Model\Country {#769
-name: "Canada"
-code: "CA"
}
-timezone: null
-providedBy: "geo_plugin"
}
]
}

Categories