setAccessible(true); $refProperty->setValue($object, $value); $refProperty->setAccessible(false); } /** * Store an image of different types * * @param string $table * @param \Illuminate\Http\UploadedFile|\SplFileInfo|string $image * @param boolean $randomizePath * @return string * @throws \ErrorException If file copy fails. */ public static function storeImage(string $table, $image, bool $randomizePath = true): string { /** @var \Illuminate\Http\UploadedFile $image */ if (\is_object($image) and get_class($image) === UploadedFile::class) { $newPath = self::prepareMoveFile($table, $image->getClientOriginalName(), $randomizePath); $newPath = str_replace(storage_path('app'), '', $newPath); $image->storePubliclyAs( pathinfo($newPath, \PATHINFO_DIRNAME), pathinfo($newPath, \PATHINFO_BASENAME) ); return 'storage' . str_replace('/public', '', $newPath); } /** @var \SplFileInfo $image */ if (\is_object($image) and get_class($image) === \SplFileInfo::class) { $newPath = self::prepareMoveFile($table, $image->getBaseName(), $randomizePath); if (!File::copy($image->getRealPath(), $newPath)) { throw new \ErrorException(sprintf( 'Failed to copy file %s to %s', $image->getRealPath(), $newPath )); } return \ltrim(str_replace(\storage_path('app/public'), '/storage', $newPath), '/'); } if (\is_string($image)) { return $image; } } /** * Prepare storage path and return * the image full path * * @param string $table Model table name. * @param string $filename The basename for the file. * @param boolean $randomizeName Use uuid in name path. * @return string */ private static function prepareMoveFile(string $table, string $filename, bool $randomizeName = true): string { $pathFolder = \storage_path(sprintf('app/public/images/%s', $table)); if (!File::exists($pathFolder)) { File::makeDirectory($pathFolder, 0755, true); } $filename = sprintf('%s-%s', $randomizeName ? Str::uuid() : 'R', $filename); $fullPath = sprintf('%s/%s', $pathFolder, $filename); if (File::exists($fullPath)) { File::delete($fullPath); } return $fullPath; } /** * Strip scheme (https) on oembed urls * Only embed allowed are for MP4 self hosted videos right now * * https is force by ckeditor MediaElements which natively supports * only cloud hosted medias (youtube, spotify etc..) * * @param string $input * @return string */ public static function stripCkEditorSchemesOnMediaInputs(string $input): string { $input = \preg_replace('/(?<=)/m', '', $input); return $input; } /** * Assert That no Back Office url is present on txt * * @param string $field * @param string $text * @return string * @throws \Illuminate\Validation\ValidationException If url is a bo url. */ public static function assertNoInternalBoUrls(string $field, string $text): string { $routes = Route::getRoutes(); // * Url replace and email replace. $text = preg_replace_callback( // phpcs:ignore Generic.Files.LineLength.TooLong '/(?P(?:http[s]?:\/\/.)?(?:www\.)?[-a-zA-Z0-9@%._\+~#=]{2,256}\.[a-z]{2,6}\b(?:[-a-zA-Z0-9@:%_\+.~#?&\/\/=]*))/', function ($matches) use ($routes, $field) { $match = collect($matches)->first() ?? ''; $url = $match; try { $request = Request::create($url); // If a route is match then it do not throws exception. if ( \parse_url(config('app.url'), \PHP_URL_HOST) === \parse_url($url, \PHP_URL_HOST) && trim($routes->match($request)->getPrefix() ?? '', '/') === 'bo' ) { // Removes the matched url. throw ValidationException::withMessages([ $field => sprintf( '%s : Il est interdit de coller une url du back office dans ce champ', trans($field) ) ]); } return $match; } catch (NotFoundHttpException $e) { return $match; } }, $text ); return $text; } /** * Assert That only external url is present on txt * * @param string $field * @param string $text * @return string * @throws \Illuminate\Validation\ValidationException If url is a bo url. */ public static function assertNoInternalUrls(string $field, string $text): string { self::assertNoInternalBoUrls($field, $text); $routes = Route::getRoutes(); // * Url replace and email replace. $text = preg_replace_callback( // phpcs:ignore Generic.Files.LineLength.TooLong '/(?P(?:http[s]?:\/\/.)?(?:www\.)?[-a-zA-Z0-9@%._\+~#=]{2,256}\.[a-z]{2,6}\b(?:[-a-zA-Z0-9@:%_\+.~#?&\/\/=]*))/', function ($matches) use ($routes, $field) { $match = collect($matches)->first() ?? ''; $url = $match; try { $request = Request::create($url); // If a route is match then it do not throws exception. if ( \parse_url(config('app.url'), \PHP_URL_HOST) === \parse_url($url, \PHP_URL_HOST) && $routes->match($request) ) { // Removes the matched url. throw ValidationException::withMessages([ $field => sprintf( '%s : Il est interdit de coller une url de %s dans ce champ', trans($field), config('app.name') ) ]); } return $match; } catch (NotFoundHttpException $e) { return $match; } }, $text ); return $text; } /** * * Register CSP report URI * * Put this function inside RouteServiceProvider, * and call it in RouteServiceProvider::boot * * ! Use this in conjonction with kwaadpepper/exception-handler * * @return void */ public static function cspReportRoute() { // * CSP REPORT URI, Please add /csp-report to VerifyCsrfToken $except middleware \Illuminate\Support\Facades\Route::post('/csp-report', function () { $isCspRequest = request()->header('Content-Type') === 'application/csp-report'; if (!$isCspRequest or !request()->json('csp-report') or !is_array(request()->json('csp-report'))) { abort(401); } // * Flatten if needed $cspReport = collect(request()->json('csp-report')) ->flatMap(function ($v, $k) { return [$k => is_array($v) ? collect($v)->flatten()->implode(', ') : $v]; })->all(); // * Filter array keys $cspReport = collect(array_intersect_key($cspReport, [ 'blocked-uri' => '', 'document-uri' => '', 'original-policy' => '', 'referrer' => '', 'violated-directive' => '' ])); report(new \Illuminate\Http\Client\HttpClientException(sprintf( "CSP REPORT for %s : \n %s", $cspReport->get('document-uri'), $cspReport->implode("\n") ))); })->middleware('throttle:5,2') ->withoutMiddleware(\App\Http\Middleware\VerifyCsrfToken::class) ->name('cspReportUri'); } }