Generate image extension from mimetype - security

I have been trying image uploading in Laravel 5 (upload generated through laravelcollective/forms, and processed using Intervention Image library).
What I wanna do is when user uploads any photo, I want to set the extension based on its mimetype. There should be some basic check to protect against spurious data injection.
$file_profile_image->getClientMimeType();
To do that, should I simply be mapping like so ?
['image/jpeg' => 'jpg', 'image/gif'=> 'gif']

I would use the Intervention package to check if you're loading a valid image and get the mime from there.
Something like this:
/**
* Store a file
*
* #return Response
*/
public function store(Filesystem $filesystem)
{
// check if file was posted
$uploadedFile = Request::file('file');
// other checks here, ->isValid() && filesize
try {
$image = Image::make(\File::get($uploadedFile));
} catch (\Intervention\Image\Exception\NotReadableException $e) {
\Log::error('Unsupported filetype');
dd('Unsupported filetype');
// return proper error here
}
// mime as returned by Intervention
$mime = $image->mime();
// other stuff
// store # fs
}

This is how I would do it:
$source_file = $request->file('image')->getRealPath();
$info = get_image_details($source_file);
The get_image_details($path) function can be defined as follows:
function get_image_details($path)
{
$details = #getimagesize( $path );
if ( is_array($details) && count($details) > 2 ) {
$info = [
'width' => $details[0],
'height' => $details[1],
'mime' => $details['mime'],
'size' => #filesize($path),
'path' => $path,
];
switch ($details['2']) {
case IMG_PNG:
case 3:
$info['type'] = 'png';
break;
case IMG_JPG:
case 2:
$info['type'] = 'jpg';
break;
case IMG_GIF:
case 1:
$info['type'] = 'gif';
break;
default:
$info['type'] = $details[2];
break;
}
return $info;
}
return false;
}

File objects have a method just for this case. All you have to do is call the guessExtension method on your file object like so
$file_profile_image->guessExtension()

Related

Get all the blobs inside sub directory inside container

So I'm having issues on getting the blobs inside my container - I'm able to successfully grab all the blobs inside a container, but not inside a folder in the container - How would I be able to successfully do that?
Problem:
I have a folder inside my container called 'big/' - How would I be able to grab all the blobs from that folder instead of just the generic container as I'm doing below?
All help would be appreciated!
So here is the getBlobs() method that I have (View the image and code below):
/**
* Get all blobs from container
*
* #param int $max
* #return array
*/
public function getBlobs(int $max = 5000) : array
{
// Check if a container is set
if (!$this->containerName) {
return [];
}
try {
$blobMeta = [];
$blobOptions = new ListBlobsOptions();
// Set the max results at 5000 (Default: 5000)
$blobOptions->setMaxResults($max);
do {
// Grab the defined container
$blob_list = $this->connect()->listBlobs(
$this->containerName,
$blobOptions
);
var_dump($blob_list);
die();
// Loop through all the
foreach ($blob_list->getBlobs() as $blob) {
$blobMeta[] = [
'name' => $blob->getName(),
'url' => $blob->getUrl(),
];
}
$blobOptions->setContinuationToken($blob_list->getContinuationToken());
} while ($blob_list->getContinuationToken());
return $blobMeta;
} catch (ServiceException $e) {
$code = $e->getCode();
$error_message = $e->getMessage();
echo $code . ": " . $error_message . PHP_EOL;
return [];
}
}
If you want to list blobs under a folder, you can use ListBlobsOptions to setPrefix("YourFolderName/") to do this. You can find a detailed code sample in listBlobsSample function at line 430 here.

Phpspreadsheet : Download doesn't work for .csv and .pdf files

I work with the Symfony 4 framework. My goal is to be able to export data with different possible formats (xlsx, ods, csv, pdf).
In my controller, I did like this:
/**
* Permet d'exporter des données.
*
* #Route("rh/export", name="rh_export")
*/
public function export(Request $request, ExportService $exportService)
{
$exportExcel = new ExportExcel();
$form = $this->createForm(ExportExcelType::class, $exportExcel);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$spreadsheet = $exportService->export($exportExcel);
switch($exportExcel->getExtension())
{
case "xlsx":
$writer = new Xlsx($spreadsheet);
break;
case "csv":
$writer = new Csv($spreadsheet);
break;
case "ods":
$writer = new Ods($spreadsheet);
break;
case "pdf":
$writer = new Dompdf($spreadsheet);
break;
default:
$writer = new Xlsx($spreadsheet);
break;
}
if($exportExcel->getExtension() === "pdf")
{
$writer->writeAllSheets();
}
// Create a Temporary file in the system
$fileName = $exportExcel->getNomFichier().'.' . $exportExcel->getExtension();
$temp_file = tempnam(sys_get_temp_dir(), $fileName);
// Create the excel file in the tmp directory of the system
$writer->save($temp_file);
// Return the excel file as an attachment
return $this->file($temp_file, $fileName, ResponseHeaderBag::DISPOSITION_INLINE);
}
return $this->render('rh/export.html.twig', [
'form' => $form->createView(),
]);
}
I analyze the submission of the form.
I create the spreadsheet
Depending on the type of file desired, I initialize the $writer
Create a Temporary file in the system
Create the excel file in the tmp directory of the system
Return the excel file as an attachment
The strange thing is that when the file type is .xlsx or .ods, the file downloads correctly.
However if it is .csv or .pdf, then the file is not downloaded, and instead it opens in my browser
EDIT : I resolved my problem with :
Replace :
// Create a Temporary file in the system
$fileName = $exportExcel->getNomFichier().'.' . $exportExcel->getExtension();
$temp_file = tempnam(sys_get_temp_dir(), $fileName);
// Create the excel file in the tmp directory of the system
$writer->save($temp_file);
// Return the excel file as an attachment
return $this->file($temp_file, $fileName, ResponseHeaderBag::DISPOSITION_INLINE);
by
$response = new StreamedResponse(
function () use ($writer) {
$writer->save('php://output');
}
);
$fileName = $exportExcel->getNomFichier().'.' . $exportExcel->getExtension();
$response->headers->set('Content-Type', 'application/vnd.ms-excel');
$response->headers->set('Content-Disposition', sprintf('attachment;filename="%s"', $fileName));
$response->headers->set('Cache-Control','max-age=0');
return $response;

How Express routes similar url links?

Developing web app with node.js and express.
I have following two urls to distinguish:
/api/v1/source?id=122323
/api/v1/source?timestamp=1555050505&count=10
I come up a naive solution. I leave such similar urls to one route method and use if eles to specify solutions, i.e:
if(id){
//solution with id
}
if(timestamp&&count){
//solution with timestamp and count but without id
}
Apparently, this is not clean. Because in the future,I may want to add new field which will make this router huge and ugly.
So How can I overcome this? Or to change url structure.I want to build a Restful api.
Try to put together all the properties in a list and use Array#every to check if all the values in Array evaluates to true.
Maybe something like this:
(( /* req, res */)=>{
// Dummy express Request Object
const req = {
params : {
//id : '123',
count : 10,
timestamp : 1555050505,
newParameter : 'whatever value'
}
}
let { params } = req;
let {
id
, count
, timestamp
, newParameter
} = params;
if(id){
console.log('Action with id');
return;
}
let secondConditionArray = [
count, timestamp, newParameter
];
if( secondConditionArray.every(Boolean) ){
console.log('Second Action')
} else {
console.log('Some values are no truthy')
}
})()
You can get Url parameters with req.params
if(req.params.id){
//solution with id
}
if(req.params.timestamp && req.params.count){
//solution with timestamp and count but without id
}

How to display flash message in Kohana 3

I have to show message after insert some data in database. I'm using Kohana. Is there a way to do that with flash messages? It's better than header refresh.
Well sort of. You could use the Session::get_once() function. But this only let you retrieve a variable once, and you cannot use it again in the same request. While you want a flash message to persist a full request cycle. To manage that you'll need a wrapper class, something like this.
class Flash {
private $session;
private $messages = array();
private static $_instance; // Singleton object
public static function instance() {
if ( ! isset( self::$_instance ) ) {
self::$_instance = new Flash();
}
return self::$_instance;
}
private function __construct() {
$this->session = Session::instance();
$this->messages['current'] = $this->session->get_once('flash');
if( ! is_array($this->messages['current'] ) ) {
$this->messages['current'] = array();
}
}
public function add( $key, $message === null ) {
if ( is_null( $message ) ) {
$message = $key;
$key = null;
}
$this->messages['new'][$key] = $message;
$this->session->set('flash', $this->messages['new'] );
return true;
}
public function get( $item = null ) {
if( $item === null ) {
return $this->messages['current'];
}
if( ! array_key_exists($item, $this->messages['current']) ) {
return null;
}
return $this->messages['current'][$item];
}
}
Usage:
$flash = Flash::instance();
$flash->add('A random message');
$flash->add('some_key', 'Some message');
$flash->get(); // array( 0 => 'A random message', 'some_key' => 'Some message')
$flash->get('some_key'); // 'A Random message'
What it does basically is on initialization it retrieves the current message from the session, using the get_once() function. The variable is nou out of the Session object, so it will only last this request. Everytime you add a variable, it will immediately persisted to the Session object.
There is just one problem; if you are using ajax calls, the messages will only be available on the initial php request, not on subsequent ajax calls. And there is also no restriction whatsoever on what kind of variable you are storing (but it must be serializable). You'll have to build in some checks for that too.
warning: the class is not tested, so it would surprise me if you do not get a syntax error ;)
And to go a step further: you would need an extra refresh anyway. The request flow should be like this imo:
Request 1: User is presented form
Request 2: User posts the form, which is processed. Data is inserted in database. When done, user is redirected
Request 3: A confirmation page is shown (can be "thank you", or the detail page, whatever).
You would set the flash message in request 2, and show it in 3. I would not directly show the thank you page on request 2, because when the user refreshes, the form will be posted again.
Use this module. Works perfectly :)

How to implement a pagination for a search module in Zend Framework 2?

I have a module Search in my ZF2 application. The user fills in a search form out and gets a list of courses.
Now I'm adding the pagination to the module. The paginator is basically working: I can retrieve data over it and the pagination is displayed correctly (pagelinks 1-7 for 70 found courses with the dafault setting 10 items per page).
But it's still not usable. When I click on a pagelink, the form POST data is lost. I know -- it cannot work the way, how I implemented it (see the code below). But I have no idea, how to do it correctly, in order to eep checking the form data and nonetheless be able to use pagination.
That is my code:
Table class Search\Model\CourseTable
class CourseTable {
...
// without pagination
// public function findAllByCriteria(CourseSearchInput $input) {
// with pagination
public function findAllByCriteria(CourseSearchInput $input, $pageNumber) {
...
$select = new Select();
$where = new Where();
$having = new Having();
...
// without pagination
// $resultSet = $this->tableGateway->selectWith($select);
// return $resultSet;
// with pagination
$adapter = new \MyNamespqce\Paginator\Adapter\DbSelect($select, $this->tableGateway->getAdapter());
$paginator = new \Zend\Paginator\Paginator($adapter);
$paginator->setCurrentPageNumber($pageNumber);
return $paginator;
}
...
}
Search\Controller\SearchController
class SearchController extends AbstractActionController {
public function searchCoursesAction() {
$form = $this->getServiceLocator()->get('Search\Form\CourseSearchForm');
$request = $this->getRequest();
if ($request->isPost()) {
$courseSearchInput = new CourseSearchInput();
$form->setInputFilter($courseSearchInput->getInputFilter());
$form->setData($request->getPost());
if ($form->isValid()) {
$courseSearchInput->exchangeArray($form->getData());
// without pagination
// $courses = $this->getCourseTable()->findAllByCriteria($courseSearchInput);
// with pagination
$page = $this->params()->fromRoute('page');
$paginator = $this->getCourseTable()->findAllByCriteria($courseSearchInput, $page);
} else {
$paginator = null;
}
} else {
$paginator = null;
}
return new ViewModel(array(
'form' => $form,
// without pagination
// 'courses' => $courses,
// with pagination
'paginator' => $paginator,
'cities' => ...
));
}
...
}
How to get it working?
I also have the same problem, and I have solved it. But this is not good way. May be the idea will help you.
I solved it as follow: (Search pagination for Zend tutorial album module)
I build two action in controller named "search" and "index".
Whenever the search form submitted, it always post the value to search action. Search action build the url with search parameters, and redirect to index to disply search result.
And when the pagination links clicked, then posted values are passed through url. So whenever index action ask for search parameters, it always get the values in same format.
I defined route as follows:
'album' => array(
'type' => 'segment',
'options' => array(
'route' => '/album[/:action][/:id][/page/:page][/order_by/:order_by][/:order][/search_by/:search_by]',
'constraints' => array(
'action' => '(?!\bpage\b)(?!\border_by\b)(?!\bsearch_by\b)[a-zA-Z][a-zA-Z0-9_-]*',
'id' => '[0-9]+',
'page' => '[0-9]+',
'order_by' => '[a-zA-Z][a-zA-Z0-9_-]*',
'order' => 'ASC|DESC',
),
'defaults' => array(
'controller' => 'Album\Controller\Album',
'action' => 'index',
),
),
),
There is a parameter named "search_by", which will keep all search parameters as a json string. This is the point, which is not good I know, but have not find any other way yet.
"Search" action build this string as -
public function searchAction()
{
$request = $this->getRequest();
$url = 'index';
if ($request->isPost()) {
$formdata = (array) $request->getPost();
$search_data = array();
foreach ($formdata as $key => $value) {
if ($key != 'submit') {
if (!empty($value)) {
$search_data[$key] = $value;
}
}
}
if (!empty($search_data)) {
$search_by = json_encode($search_data);
$url .= '/search_by/' . $search_by;
}
}
$this->redirect()->toUrl($url);
}
And next index action decode the string, do necessary action, and also send the json string to view.
public function indexAction() {
$searchform = new AlbumSearchForm();
$searchform->get('submit')->setValue('Search');
$select = new Select();
$order_by = $this->params()->fromRoute('order_by') ?
$this->params()->fromRoute('order_by') : 'id';
$order = $this->params()->fromRoute('order') ?
$this->params()->fromRoute('order') : Select::ORDER_ASCENDING;
$page = $this->params()->fromRoute('page') ? (int) $this->params()->fromRoute('page') : 1;
$select->order($order_by . ' ' . $order);
$search_by = $this->params()->fromRoute('search_by') ?
$this->params()->fromRoute('search_by') : '';
$where = new \Zend\Db\Sql\Where();
$formdata = array();
if (!empty($search_by)) {
$formdata = (array) json_decode($search_by);
if (!empty($formdata['artist'])) {
$where->addPredicate(
new \Zend\Db\Sql\Predicate\Like('artist', '%' . $formdata['artist'] . '%')
);
}
if (!empty($formdata['title'])) {
$where->addPredicate(
new \Zend\Db\Sql\Predicate\Like('title', '%' . $formdata['title'] . '%')
);
}
}
if (!empty($where)) {
$select->where($where);
}
$album = $this->getAlbumTable()->fetchAll($select);
$totalRecord = $album->count();
$itemsPerPage = 2;
$album->current();
$paginator = new Paginator(new paginatorIterator($album));
$paginator->setCurrentPageNumber($page)
->setItemCountPerPage($itemsPerPage)
->setPageRange(7);
$searchform->setData($formdata);
return new ViewModel(array(
'search_by' => $search_by,
'order_by' => $order_by,
'order' => $order,
'page' => $page,
'paginator' => $paginator,
'pageAction' => 'album',
'form' => $searchform,
'totalRecord' => $totalRecord
));
}
All the sorting and paging url contain that string.
If you know all the searching paarameters before, then you can define that at route, and pass like the same way without json string. As I have to build a common search, I have build a single string.
Source code for "Album search" is available in git hub at https://github.com/tahmina8765/zf2_search_with_pagination_example.
Live Demo: http://zf2pagination.lifencolor.com/public/album
#Sam & #automatix in the question comments are both right. My suggestion (though I'm looking for a simpler alternative) is to construct a segment route, which covers all of the options that you're likely to need and start with a standard form POST request.
Then, after the request is validated, pass the form data to the paginationControl helper as follows:
$resultsView = new ViewModel(array(
'paginator' => $paginator,
'routeParams' => array_filter($form->getData())
));
Then, in your view template, set the route parameters in the paginationControl view helper:
<?php echo $this->paginationControl($paginator, 'Sliding', 'paginator/default',
array('routeParams' => $routeParams)
) ?>
I've used array_filter here because it's a really simple way of removing any element from the form data that's null, empty or so on. That way you don't pass in extra data that you don't need.

Resources