I want that Apache and Pinax only deliver Attachments to authenticated users.
I found this post, but i can't make it work.
My Apache-conf-file:
WSGIPythonPath /usr/local/bin/python
<VirtualHost *:80>
ServerName www.domain.com
ServerAlias domain.com
WSGIDaemonProcess k-production python-path=/path/to/app/pinax-env/lib/python2.6/site-packages
WSGIProcessGroup k-production
Alias /site_media /path/to/app/cp/site_media
<Directory /path/to/app/cp/site_media>
Order deny,allow
Allow from all
</Directory>
WSGIScriptAlias /site_media/media/attachments /path/to/app/cp/deploy/pinax.wsgi
<Directory /path/to/app/cp/site_media/media/attachments>
Deny from all
</Directory>
XSendFile On
XSendFileAllowAbove On
WSGIScriptAlias / /path/to/app/cp/deploy/pinax.wsgi
<Directory /path/to/app/cp/deploy>
Order deny,allow
Allow from all
</Directory>
</VirtualHost>
and my (still rough) view, that should get called:
#login_required
def sendfile(request, slug):
app, content_object_id, img = slug.split('/')
project_file = get_object_or_404(Attachment, attachment_file = 'attachments/'+slug)
response = HttpResponse()
response['X-Sendfile'] = os.path.join(settings.MEDIA_ROOT, 'attachments/'+slug)
content_type = 'application/octet-stream'
response['Content-Type'] = content_type
response['Content-Disposition'] = 'attachment; filename="%s"' % os.path.basename(os.path.join(settings.MEDIA_ROOT, 'attachments/'+slug))
return response
Apache throws a 403 no matter if the user is logged in.
Via the develoment-server i can access the view, but no data will get transmitted.
What is wrong?
I was trying to do pretty much exactly the same thing, and the solution turned out to be not using WSGIScriptAlias, and instead using a regular Alias to a directory that defined a wsgi handler. For the view I basically just wrote a wrapper around django.views.static.serve.
My apache conf ended up looking like this:
# myproject
<VirtualHost *:8080>
#DocumentRoot /var/www/myproject/public
ServerName myproject
ErrorLog /var/www/myproject/logs/apache_error_log
CustomLog /var/www/myproject/logs/apache_access_log common
AliasMatch ^/(media/uploads/protected/.*) /var/www/myproject/src/myproject-trunk/server/django.wsgi/$1
Alias /media/ /var/www/myproject/public/media/
Alias / /var/www/myproject/src/myproject-trunk/server/django.wsgi/
<Directory /var/www/myproject/src/myproject-trunk/server>
Options ExecCGI
AddHandler wsgi-script .wsgi
# WSGIApplicationGroup %{GLOBAL}
Order allow,deny
Allow from all
</Directory>
<Directory /var/www/myproject/public/media>
Order deny,allow
Allow from all
</Directory>
</VirtualHost>
Try to concentrate on the development server first - since it is a simpler setup and thus less error prone.
Perhaps try this:
#login_required def sendfile(request, slug):
## these are never used
# app, content_object_id, img = slug.split('/')
# project_file = get_object_or_404(Attachment, attachment_file = 'attachments/'+slug)
response = HttpResponse()
response['X-Sendfile'] = os.path.join(settings.MEDIA_ROOT, 'attachments/'+slug)
import pdb; pdb.set_trace()
# your development server will stop here
# you can now inspect the your context, e.g.
# >> p response['X-Sendfile']
# this should print the value of response['X-Sendfile']
# >> c
# this will continue program execution
# for more commands see http://www.python.org/doc/2.4/lib/debugger-commands.html
content_type = 'application/octet-stream'
response['Content-Type'] = content_type
# Content-Disposition filename is only for suggesting a name for the file
# when the user tries to download it, e.g.:
# response['Content-Disposition'] = 'attachment; filename='localfile.txt'
response['Content-Disposition'] = 'attachment; filename="%s"' % os.path.basename(os.path.join(settings.MEDIA_ROOT, 'attachments/'+slug))
return response
But by using the dev server you won't get any files served, since Apache will do the file serving.
You can just ensure, the data sent to apache is correct.
Or use (not recommended for your production server)
f = open(os.path.join(settings.MEDIA_ROOT, 'attachments/'+slug), 'rb')
response = HttpResponse(f.read())
instead of
response = HttpResponse()
response['X-Sendfile'] = os.path.join(settings.MEDIA_ROOT, 'attachments/'+slug)
Some Links:
Similar topic at stackoverflow
Related
I am trying to make a project and have some media files which should only be accessed by their owner.
In production, media and static files are served by apache (or nginx but I am using apache).
I was looking for some solutions and I am not able to apply.
On djangosnippets website, I found this code,
from mod_python import apache
from django.core.handlers.base import BaseHandler
from django.core.handlers.modpython import ModPythonRequest
class AccessHandler(BaseHandler):
def __call__(self, req):
from django.conf import settings
# set up middleware
if self._request_middleware is None:
self.load_middleware()
# populate the request object
request = ModPythonRequest(req)
# and apply the middleware to it
# actually only session and auth middleware would be needed here
for middleware_method in self._request_middleware:
middleware_method(request)
return request
def accesshandler(req):
os.environ.update(req.subprocess_env)
# check for PythonOptions
_str_to_bool = lambda s: s.lower() in ("1", "true", "on", "yes")
options = req.get_options()
permission_name = options.get("DjangoPermissionName", None)
staff_only = _str_to_bool(
options.get("DjangoRequireStaffStatus", "on")
)
superuser_only = _str_to_bool(
options.get("DjangoRequireSuperuserStatus", "off")
)
settings_module = options.get("DJANGO_SETTINGS_MODULE", None)
if settings_module:
os.environ["DJANGO_SETTINGS_MODULE"] = settings_module
request = AccessHandler()(req)
if request.user.is_authenticated():
if superuser_only and request.user.is_superuser:
return apache.OK
elif staff_only and request.user.is_staff:
return apache.OK
elif permission_name and request.user.has_perm(
permission_name
):
return apache.OK
return apache.HTTP_UNAUTHORIZED
But I am not able to install mod_python. Please tell me how to do that first
And I changed my .conf file for apache which is as below.
<VirtualHost *:80>
ServerName podcast.com
ServerAdmin srpatel980#gmail.com
DocumentRoot /home/username/Documents/secret_media
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
Alias /static /home/username/Documents/secret_media/static
<Directory /home/username/Documents/secret_media/static>
Require all granted
</Directory>
Alias /media /home/username/Documents/secret_media/media
<Directory /home/username/Documents/secret_media/media>
Require all granted
</Directory>
<Directory /home/username/Documents/secret_media/secret_media>
<Files wsgi.py>
Require all granted
</Files>
</Directory>
WSGIScriptAlias / /home/username/Documents/secret_media/secret_media/wsgi.py
WSGIDaemonProcess secret_media python-path=/home/username/Documents/secret_media python-home=/home/username/Documents/secret_media/venv
WSGIProcessGroup secret_media
LoadModule auth_basic_module modules/mod_auth_basic.so
LoadModule authz_user_module modules/mod_authz_user.so
<Location "/media/images">
PythonPath /home/username/Documents/secret_media
PythonOption DJANGO_SETTINGS_MODULE secret_media.settings
PythonAccessHandler secret_media.wsgi.py #this should point to accesshandler
SetHandler None
</Location>
</VirtualHost>
There are some settings which are repeated, don't know why, please explain
X-Sendfile
I do not know about the method you use above, but I have been using mod_xsendfile to do the same. Whats the principle: request to a url is handeled by a view that checks access rights ... the view returns a response with a key "X-Sendfile" & file ... this triggers Apache on the way back to serve the media file.
I just show you the code without testing syntax .... please ask if something is not clear
Apache httpd.conf
LoadModule xsendfile_module modules/mod_xsendfile.so
in Apache remove the usual "alias media ..."
Apache httpd-vhosts.conf
<VirtualHost *:80>
# ... all your usual Django Config staff
# remove the usual alias /media/
# Alias /media/ d:/WEBSPACES/dieweltdahinter_project/media/
XSendFile on
XSendFilePath D:/your_path/media/
<Directory "D:/your_path/media">
Order Deny,Allow
Allow from all
</Directory>
</VirtualHost>
urls.py
urlpatterns = [
.....,
re_path(r'^media/(?P<folder>[A-Za-z0-9\-_]+)/(?P<filename>[A-Za-z0-9\-_]+).(?P<extension>[A-Za-z0-9]+)\/?$', app_views.media_xsendfile, name='media-xsendfile'),
.....
]
views.py
# add decorators to manage access
#
def media_xsendfile(request, folder='', filename=None, extension=None):
# add an kind of user check ....
# only server certain type of files
if extension in ('jpg', 'png', 'gif'):
response['Content-Type'] = 'image/'+extension
elif extension == 'mp3':
response['Content-Type'] = 'audio/mpeg'
else:
return
if not folder == '':
folder = '/'+folder+'/'
response = HttpResponse()
# this is the part of the response that Apache reacts upon:
response['X-Sendfile'] = smart_str(settings.MEDIA_ROOT + folder + filename + "." + extension)
# need to handover an absolute path to your file to Apache!!
return response
One way to do it is using a so-called X-Sendfile.
In simple words:
User requests URL to get a protected file (so you need to keep your public and protected files separately, and then either proxy a request to Django for protected files, or serve files directly from the apache/nginx if they are public)
Django view decides which file to return based on URL, and checks user permission, etc.
Django returns an HTTP Response with the 'X-Sendfile' header set to the server's file path
The web server finds the file and returns it to the requester
The setup will be different for nginx and apache, according to my findings you need to install mod_xsendfile for apache, nginx supports it out of the box. Hopefully, that helps, ask any additional questions if needed.
I have a function in apps.py set to run at Apache boot/restart, however it doesn't and it only works after I pull up the index page.
However, if I use Django's development environment it works perfectly.
APPS.PY
from django.apps import AppConfig
class GpioAppConfig(AppConfig):
name = 'gpio_app'
verbose_name = "My Application"
def ready(self):
from apscheduler.schedulers.background import BackgroundScheduler
from gpio_app.models import Status, ApsScheduler
import gpio_app.scheduler as sched
import logging
logging.basicConfig()
logging.getLogger('apscheduler').setLevel(logging.DEBUG)
sched.cancel_day_schedule()
sched.get_schedule()
sched.daily_sched_update()
sched.add_status_db()
MOD_WSIG 000-default.conf is as follows:
<VirtualHost *:80>
ServerName 127.0.0.1
ServerAlias localhost
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
Alias /robots.txt /path/to/mysite.com/static/robots.txt
Alias /favicon.ico /path/to/mysite.com/static/favicon.ico
Alias /static /home/pi/poolprojectdir/static
<Directory /home/pi/poolprojectdir/static>
Require all granted
</Directory>
<Directory /home/pi/poolprojectdir/poolproject>
<Files wsgi.py>
Require all granted
</Files>
</Directory>
WSGIDaemonProcess poolproject python- home=/home/pi/poolprojectdir/venv python-path=/home/pi/poolprojectdir
WSGIProcessGroup poolproject
WSGIScriptAlias / /home/pi/poolprojectdir/poolproject/wsgi.py
Any ideas as to how I get apps.py recognised by Apache2?
The problem is with apache loading your wsgi.py lazily - only after the first request arrives.
See this answer on how to fix this.
Apache or Python doesn't import image on the media folder. When I tried to upload something It returns 500 error.
Django is working behind Apache with mod_wsgi on CentOS 7. Django also works in a virtual environment. I disabled all firewalls (selinux, firewalld and iptables) I gave every permission to the Apache for the project folder. (chown apache:apache and chmod 777 I applied public write permission only for the media folder)
Here is my apache conf file for the project:
<VirtualHost *:80>
ServerAdmin webmaster#domain.com
ServerName example.com
ServerAlias www.example.com
DocumentRoot /var/www/example.com/html
Alias /static/ /var/www/example.com/html/static/
Alias /media /var/www/example.com/html/media
<Location "/static/">
Options -Indexes
</Location>
<Directory /var/www/example.com/html/logging>
<Files debug.log>
Require all granted
</Files>
</Directory>
<Directory /var/www/example.com/html>
<Files db.sqlite3>
Require all granted
</Files>
</Directory>
<Directory /var/www/example.com/html/media>
Require all granted
</Directory>
<Directory /var/www/example.com/html/media/uploads>
Require all granted
</Directory>
<Directory /var/www/example.com/html/static>
Require all granted
</Directory>
<Directory /var/www/example.com/html/example>
<Files wsgi.py>
Require all granted
</Files>
</Directory>
WSGIDaemonProcess example.com python-path=/var/www/example.com/html:/var/www/example.com/html/env/lib/python3.4/site-packages
WSGIProcessGroup example.com
WSGIScriptAlias / /var/www/example.com/html/example/wsgi.py
#Log Settings
ErrorLog "/etc/httpd/logs/example_error"
CustomLog "/etc/httpd/logs/example_acess" combined
</VirtualHost>
model.py
from django.db import models
from services.file_extentions import validate_image_extention
# Create your models here.
class FotografModel(models.Model):
"""Model definition for GaleriModel."""
# TODO: Define fields here
foto = models.ImageField(verbose_name="Fotoğraf", upload_to='uploads/fotograflar/', max_length=100, height_field=None, width_field=None, validators=[validate_image_extention])
foto_baslik = models.CharField(verbose_name="Fotoğraf Açıklaması (Görme Engelliler için)", max_length=100, blank=True, null=True)
olusturulma_tarihi = models.DateTimeField(editable=False, auto_now_add=True, null=True, verbose_name="Oluşturulma tarihi")
guncelleme = models.DateTimeField(auto_now=True, null=True, auto_now_add=False, verbose_name="Son güncelleme", editable=False)
class Meta:
"""Meta definition for FotografModel."""
verbose_name = 'Fotoğraf'
verbose_name_plural = 'Fotoğraflar'
def image_tag(self):
path = "/media/"+str(self.foto)
return u'<img src="%s" style="height:80px!important;height:auto;"/>' % (path)
image_tag.short_description = "Fotoğraf"
image_tag.allow_tags = True
def __str__(self):
return "%s" % (self.foto_baslik)
from django.db.models.signals import pre_delete
from django.dispatch.dispatcher import receiver
#receiver(pre_delete, sender=FotografModel)
def fotograf_delete(sender, instance, **kwargs):
# Pass false so FileField doesn't save the model.
instance.foto.delete(False)
debug log
from . import _imaging as core
ImportError: cannot import name '_imaging'
When I open the shell in my virtual environment and tried this code from PIL import _imaging it works well. The project is using 4.3.0 version of Pillow package.
When I run
sudo service apache2 restart
I get an error
unable to resolve host hostname
When I look at the error.txt it says it's my WSGI configuration.
Here is my WSGI script.
import os
import sys
from django.core.handlers.wsgi import WSGIHandler
import django
os.environ['DJANGO_SETTINGS_MODULE'] = 'app.settings'
sys.executable = '/usr/lib/python2.7/dist-packages'
application = WSGIHandler()
django.setup()
EDIT1:
Here is my apache2 file. I did change the WSGI file to /user/app/app_settings/wsgi.py
<VirtualHost website:80>
ServerName website.com
ServerAlias www.website.com
ServerAdmin email#website.com
DocumentRoot /home/user/app/static
WSGIScriptAlias / /home/user/app/app_settings/wsgi.py
<Directory /home/user/app/>
Order allow,deny
Allow from all
Require all granted
</Directory>
#Alias /robots.txt /var/www/user/app/robots.txt
#Alias /favicon.ico /var/www/user/app/favicon.ico
#Alias /images /home/user/app/static/images
#Alias /static /home/user/app/static/
ErrorLog /var/www/website/error.log
#CustomLog /var/www/website/access.log combined
</VirtualHost>
I am trying to specify the access to a certain django view only to a client calling from a VPN IP (10.8.0.3 )
My django server is supported by apache using the following .conf
<VirtualHost *>
ServerAdmin webmaster#demo.cl
DocumentRoot /home/project/virtualenvs/env1
ServerName client1.project.cl
ServerAlias www.client1.project.cl
ErrorLog /var/log/apache2/error.log
CustomLog /var/log/apache2/access.log combined
<Location "/">
SetHandler python-program
PythonHandler virtualhandler
SetEnv DJANGO_SETTINGS_MODULE project.settings
PythonOption django.root
SetEnv SITE_CLIENT_ID client1
PythonDebug On
PythonPath "['/home/project/virtualenvs/env1/django-site','/home/project/virtualenvs/env1/bin'] + sys.path"
</Location>
Alias /media "/home/project/virtualenvs/env1/lib/python2.6/site-packages/django/contrib/admin/media/"
<Location /media>
SetHandler None
</Location>
<Location /nodesaccess >
order Deny,Allow
Deny from all
Allow from 10.8.0.3
SetHandler python-program
PythonHandler virtualhandler
SetEnv DJANGO_SETTINGS_MODULE project.settings
PythonOption django.root
SetEnv SITE_CLIENT_ID client1
PythonDebug On
PythonPath "['/home/project/virtualenvs/env1/django- site','/home/project/virtualenvs/env1/bin'] + sys.path"
</Location>
</VirtualHost>
This previous configuration allows to create many django applications depending of the url, I recover the env variable and then apache load a certain setting.py which is exclusive and depends of the subdomain. Very interesting
Everything works fine (my applications) except that the access can not be denied using the "Allow from 10.8.0.3"
Any ideas?
Thank you
You can implement a simple middleware which will block any requests outside allowed IP addresses:
from django.conf import settings
from django.core.urlresolvers import reverse, NoReverseMatch
from django.http import Http404
class InternalUseOnlyMiddleware(object):
def process_request(self, request):
try:
admin_index = reverse('admin:index')
except NoReverseMatch:
return
if not request.path.startswith(admin_index):
return
remote_addr = request.META.get(
'HTTP_X_REAL_IP', request.META.get('REMOTE_ADDR', None))
if not remote_addr in settings.INTERNAL_IPS and not settings.DEBUG:
raise Http404
Original source: https://djangosnippets.org/snippets/2095/
You can use REMOTE_ADDR from HttpRequest.META (http://docs.djangoproject.com/en/dev/ref/request-response/) to check the requester IP in your view. And if it is different form the one you want just return 404 or 403 page.