protecting user uploaded files django - python

How can I allow users to upload files to their own, user designated folder, and only see files that they have uploaded? I am using django file-transfer. Currently it gives me a choice of what file to put the media in, but I can put it in any user's file and view every user's media. Here is my uploads/models.py:
from django.db import models
from django.contrib.auth.models import User, UserManager
def uploadmodel_file_upload_to(instance, filename):
print 'instance.user.username = '+ str(instance.user.username)
return 'uploads/%s/%s' % (instance.user.username, filename)
class UploadModel(models.Model):
user = models.ForeignKey('auth.user')
file = models.FileField(upload_to=uploadmodel_file_upload_to)

uploadmodel_file_upload_to returns a relative path. To build the full path, django prepends settings.MEDIA_ROOT. MEDIA_ROOT is supposed to be public readable.
So we want to save the file outside MEDIA_ROOT. Add something like this to settings.py:
import os.path
PROJECT_ROOT=os.path.abspath(os.path.dirname(__file__))
PROTECTED_MEDIA_ROOT=os.path.join(PROJECT_ROOT, 'protected_uploads')
Now you can update uploadmodel_file_upload_to to return an absolute path:
def uploadmodel_file_upload_to(instance, filename):
return '%s/%s/%s' % (settings.PROTECTED_MEDIA_ROOT, instance.user.username,
filename)
Now that the files are saved in /project/path/protected_uploads, we need to add a view to serve it, for example:
import os
import mimetypes
from django import shortcuts
from django import http
from django.conf import settings
from django.views.static import was_modified_since
from django.utils.http import http_date
from .models import *
def serve_upload(request, upload_id):
upload = shortcuts.get_object_or_404(UploadModel, pk=upload_id)
fullpath = upload.file.path
if request.user != upload.user:
return http.HttpResponseForbidden()
statobj = os.stat(fullpath)
mimetype, encoding = mimetypes.guess_type(fullpath)
mimetype = mimetype or 'application/octet-stream'
if not was_modified_since(request.META.get('HTTP_IF_MODIFIED_SINCE'),
statobj.st_mtime, statobj.st_size):
return http.HttpResponseNotModified(mimetype=mimetype)
response = http.HttpResponse(open(fullpath, 'rb').read(), mimetype=mimetype)
response["Last-Modified"] = http_date(statobj.st_mtime)
response["Content-Length"] = statobj.st_size
if encoding:
response["Content-Encoding"] = encoding
return response
And a URL:
url(r'serve_upload/(?P<upload_id>\d+)/$', 'serve_upload'),

Related

Could I use InMemoryUploadedFile directly to upload Image to Imgur?

I have some problems about uploading image to Imgur by using Django.
I have finished that Image File uploaded from react and delivered File data to backend via Axios.
But the object I got from the request.data in Python backend is InMemoryUploadedFile.
I don't want to store any image files in my disk.
Could I use directly this file which is a InMemoryUploadedFile type to upload by Imgur upload function upload_from_path?
If so, how can I do?
Here are my code,
from django.shortcuts import render
from django.http import JsonResponse
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated, IsAdminUser
from rest_framework.response import Response
from django.contrib.auth.models import User
from base.models import InCome, IncomeMoneyCategory, IncomeContributeContext,OutcomeMoneyCategory, OutcomeContributeContext, Member, Student, OutCome
from django.db.models import F, Sum
from base.serializers import IncomeSerializer, OutcomeSerializer
from rest_framework import status
from datetime import datetime
import configparser
import base.config as env
import os
from django.core.files.storage import default_storage
from django.core.files.base import ContentFile
from django.conf import settings
from imgurpython import ImgurClient
#api_view(['POST'])
def upload_image(request):
data = request.data
print(data)
album = env.IMGUR_ALBUM
print(data['image'])
image=data['image']
print("--")
print(type(image))
image_path=image
config = configparser.ConfigParser()
path = '/'.join((os.path.abspath(__file__).replace('\\', '/')).split('/')[:-1])
config.read(os.path.join(path, 'auth.ini'))
#config.read('auth.ini')
client_id = config['credentials']['client_id']
client_secret = config['credentials']['client_secret']
refresh_token = config['credentials']['refresh_token']
access_token = config['credentials']['access_token']
client = ImgurClient(client_id,client_secret, refresh_token)
client.set_user_auth(access_token, refresh_token)
if client:
config={
'album':album,
'name':'Ezra',
'title':'Test',
'description': 'Test {0}'.format(datetime.now())
}
print("Uploading image")
image = client.upload_from_path(str(image_path),config=config,anon=False)
print(image['link'])
print("Done")
return image
else:return "Error"
The type of image is <class 'django.core.files.uploadedfile.InMemoryUploadedFile'>.
But the first one parameter in the function ImgurClient.upload_from_path of imgurpython package needs the file name to upload.
So when I execute this code, I'll get this Error:
No such file or directory:...
Hope someone can help me to solve it, and sorry for my poor English.
Thank you for reading.
I have solved it later.
I used default_storage to save the file and gave the directory as the first parameter in the upload_from_path method.
After uploading to Imgur, I deleted the files and return link back.
Here is my code:
def upload_image(request):
data = request.data
album = env.IMGUR_ALBUM
image=data['image']
file = data['image']
filename = default_storage.save(file.name, ContentFile(file.read()))
config = configparser.ConfigParser()
path = '/'.join((os.path.abspath(__file__).replace('\\', '/')).split('/')[:-1])
config.read(os.path.join(path, 'auth.ini'))
client_id = config['credentials']['client_id']
client_secret = config['credentials']['client_secret']
refresh_token = config['credentials']['refresh_token']
access_token = config['credentials']['access_token']
client = ImgurClient(client_id,client_secret, refresh_token)
client.set_user_auth(access_token, refresh_token)
if client:
config={
'album':album,
'name':'Ezra',
'title':'Test',
'description': 'Test {0}'.format(datetime.now())
}
print("Uploading image")
image = client.upload_from_path(settings.MEDIA_ROOT+'/'+filename,config=config,anon=False)
print("Done")
default_storage.delete(filename)
return Response(image['link'])
else:return "Error"

Saving PDFs to disk as they are generated with django-wkhtmltopdf

What I'm trying to implement is this:
User sends query parameters from React FE microservice to the Django BE microservice.
URI is something like /api/reports?startingPage=12&dataView=Region
These PDFs are way too big to be generated in FE, so doing it server side
Request makes its way into the view.py where the data related to dataView=Region is queried from the database, each row is iterated through and a PDF report is generated for each item
Each dataView=Region can consist of a few hundred items and each of those items is its own report that can be a page long or several pages long
As the reports are generated, they should be saved to the server persistent volume claim and not be sent back to FE until they have all run.
When they have all run, I plan to use pypdf2 to combine all of the PDFs into one large file.
At that point, the file is sent back to the FE to download.
I'm only working on 1. and 3. at this point and I'm unable to:
Get the files to save to storage
Prevent the default behavior of the PDF being sent back to the FE after it has been generated
The PDFs are being generated, so that is good.
I'm trying to implement the suggestions as found here, but I'm not getting the desired results:
Save pdf from django-wkhtmltopdf to server (instead of returning as a response)
This is what I currently have on the Django side:
# urls.py
from django.urls import path
from .views import GeneratePDFView
app_name = 'Reports'
urlpatterns = [
path('/api/reports',
GeneratePDFView.as_view(), name='generate_pdf'),
]
# views.py
from django.conf import settings
from django.views.generic.base import TemplateView
from rest_framework.permissions import IsAuthenticated
from wkhtmltopdf.views import PDFTemplateResponse
# Create your views here.
class GeneratePDFView(TemplateView):
permission_classes = [IsAuthenticated]
template_name = 'test.html'
filename = 'test.pdf'
def generate_pdf(self, request, **kwargs):
context = {'key': 'value'}
# generate response
response = PDFTemplateResponse(
request=self.request,
template=self.template_name,
filename=self.filename,
context=context,
cmd_options={'load-error-handling': 'ignore'})
self.save_pdf(response.rendered_content, self.filename)
# Handle saving the document
# This is what I'm using elsewhere where files are saved and it works there
def save_pdf(self, file, filename):
with open(settings.PDF_DIR + '/' + filename, 'wb+') as destination:
for chunk in file.chunks():
destination.write(chunk)
# settings.py
...
DOWNLOAD_ROOT = '/mnt/files/client-downloads/'
MEDIA_ROOT = '/mnt/files/client-submissions/'
PDF_DIR = '/mnt/files/pdf-sections/'
...
I should note the other DOWNLOAD_ROOT and MEDIA_ROOT are working fine where the app uses them. I've even tried using settings.MEDIA_ROOT because I know it works, but still nothing is saved there. But as you can see, I'm starting out super basic and haven't added a query, loops, etc.
My save_pdf() is different than the SO question I linked to because that is what I'm using in other parts of my application and it is saving files fine there. I did try what they provided in the SO question, but had the same results with it not saving. That being:
with open("file.pdf", "wb") as f:
f.write(response.rendered_content)
So what do I need to do to get these PDFs to save to disk?
Perhaps I need to be using a different library for my needs as django-wkhtmltopdf seems to do a number of things out of the box that I don't want that I'm not clear I can override.
OK, my smooth brain gained a few ripples overnight and figured it out this morning:
# views.py
class GeneratePDFView(TemplateView):
permission_classes = [IsAuthenticated]
def get(self, request, *args, **kwargs):
template_name = 'test.html'
filename = 'test.pdf'
context = {'key': 'value'}
# generate response
response = PDFTemplateResponse(
request=request,
template=template_name,
filename=filename,
context=context,
cmd_options={'load-error-handling': 'ignore'})
# write the rendered content to a file
with open(settings.PDF_DIR + '/' + filename, "wb") as f:
f.write(response.rendered_content)
return HttpResponse('Hello, World!')
This saved the PDF to disk and also did not respond with the PDF. Obviously a minimally functioning example that I can expand on, but at least got those two issues figured out.

How to export zip in django admin?

I have images fields in my model. How to I can export to zip from my django admin. Here is my code
from django.db import models
class ModelWithImage(models.Model):
image = models.ImageField(
upload_to='images',
)
Admin
from django.contrib import admin
from .models import ModelWithImage
admin.site.register(ModelWithImage)
Something like this is used to create zipfile for content in files (path) in filenames.
response = HttpResponse(content_type='application/zip')
zip_file = zipfile.ZipFile(response, 'w')
for filename in filenames:
zip_file.write(filename)
response['Content-Disposition'] = 'attachment; filename={}'.format(zipfile_name)
return response
This code is taken from here, you may have a look for better understanding.
Although I think above code should solve your problem.

Using Django Rest Framework as security layer for file system processing

I'm trying to protect my remote server file system from unauthorized users.
I have a remote storage on a different server that store and process PDF's and PNG's from all kind of processes.
I'm using Python 2.7 with Django 1.8 and Django Rest Framework.
I trying to implement very basic, "Proxy Layer" that will give my control on who ever use file system.
This is my view.py:
from django.conf import settings
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import permissions
import requests
class Reports(APIView):
permission_classes = (permissions.AllowAny,) #Thats only for now...
def get(self, request, ssn, validity, file):
response = requests.get(settings.PROXY_BASE_URL + "/reports/" + ssn + "/" + validity + "/" + file)
return Response(response)
This concept works for any other GET POST PUT DELETE request that is text based response (For example json response from the remote server).
My problem is when I call this view, I get in the browser the default REST method definition page.
This is the response screenshot
As #AlexMorozov said in his comment, you should revert back to HttpResponse.
You can see by exploring this Django Snippet.
Here is the code as I see it:
from django.conf import settings
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import permissions
from django.http import HttpResponse
import requests
import mimetypes
class Reports(APIView):
permission_classes = (permissions.AllowAny,)
def get(self, request, ssn, validity, file):
response = requests.get(settings.PROXY_BASE_URL + "/reports/" + ssn + "/" + validity + "/" + file)
mimetype = mimetypes.guess_type(settings.PROXY_BASE_URL + "/reports/" + ssn + "/" + validity + "/" + file) #Return an array
return HttpResponse(response, content_type=mimetype[0])
Give it a go. Good luck :)
The problem is you can't simply wrap a requests response with a DRF Response and get it done. Change your code to:
from django.core.servers.basehttp import FileWrapper
upstream_response = requests.get(settings.PROXY_BASE_URL + "/reports/" + ssn + "/" + validity + "/" + file)
response = Response(FileWrapper(upstream_response.content), content_type='YOUR_MIME_TYPE')
response['Content-Disposition'] = 'attachment; filename="%s"' % 'your_filename.ext'
return response
BTW, there are other (more clean, IMO) ways to solve this particular task. Take a look at django-sendfile app. It offloads the actual file serving to a nginx or the server of your choice, while allowing you to check permissions and stuff.

python script to handle uploaded file via http post

I am working on a django project where i try to upload a file via http post request.
my upload script is :
url=r'http://MYSITEURL:8000/upload'
files={'file':open('1.png','rb')}
r=requests.post(url,files=files)
my receiving side is in my django site , in views.py:
def upload_image(request):
from py_utils import open_py_shell;open_py_shell.open_py_shell()
when i do request.FILES i can see all the post details,
what i want to know is how to save it in the server side once i got the post request
What you have in request.FILES is InMemoryUploadedFile. You just need to save it somewhere in file system.
This is example method taken from Django docs:
def handle_uploaded_file(f):
with open('some/file/name.txt', 'wb+') as destination:
for chunk in f.chunks():
destination.write(chunk)
I think you can work with models well. It will be the right way for Django. Here is an example, models.py file:
from django.db import models
from django.conf import settings
import os
import hashlib
def instanced_file(instance, filename):
splitted_name = filename.split('.')
extension = splitted_name[-1]
return os.path.join('files', hashlib.md5(str(instance.id).encode('UTF-8')).hexdigest() + '.' + extension)
class File(models.Model):
name = models.FileField('File', upload_to = instanced_file)
def get_file_url(self):
return '%s%s' % (settings.MEDIA_URL, self.name)
def __str__(self):
return self.name
def __unicode__(self):
return self.name
After the creating models create forms and go on.

Categories