This commit is contained in:
parent
019559f0c1
commit
88f787afe2
|
@ -1,9 +1,7 @@
|
|||
from django.conf import settings
|
||||
from django.contrib import admin
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
|
||||
from django.urls import include, path, re_path
|
||||
from django.views.static import serve as static_serve
|
||||
|
||||
from .api.urls import urlpatterns as api_urls
|
||||
from .base.urls import access_urlpatterns, import_export_urlpatterns
|
||||
|
@ -22,6 +20,7 @@ from .views import (
|
|||
HomePageView,
|
||||
ManageAddView,
|
||||
ManageView,
|
||||
MediaView,
|
||||
login,
|
||||
logout,
|
||||
menu_json,
|
||||
|
@ -34,13 +33,7 @@ urlpatterns = [
|
|||
path('manage/', manager_required(ManageView.as_view()), name='manage-home'),
|
||||
re_path(r'^manage/menu.json$', manager_required(menu_json), name='menu-json'),
|
||||
path('manage/add', manager_required(ManageAddView.as_view()), name='add-connector'),
|
||||
re_path(
|
||||
r'^media/(?P<path>.*)$',
|
||||
login_required(static_serve),
|
||||
{
|
||||
'document_root': settings.MEDIA_ROOT,
|
||||
},
|
||||
),
|
||||
re_path(r'^media/(?P<path>.*)$', manager_required(MediaView.as_view()), name='media'),
|
||||
re_path(r'^admin/', admin.site.urls),
|
||||
re_path(r'^manage/access/', decorated_includes(manager_required, include(access_urlpatterns))),
|
||||
re_path(r'^manage/', decorated_includes(manager_required, include(import_export_urlpatterns))),
|
||||
|
|
|
@ -19,6 +19,7 @@ import hashlib
|
|||
import inspect
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import uuid
|
||||
from urllib.parse import quote
|
||||
|
||||
|
@ -29,6 +30,7 @@ from django.contrib.auth import logout as auth_logout
|
|||
from django.contrib.auth import views as auth_views
|
||||
from django.core.cache import cache
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.core.files.storage import DefaultStorage
|
||||
from django.db import transaction
|
||||
from django.db.models import Q
|
||||
from django.http import Http404, HttpResponse, HttpResponseRedirect
|
||||
|
@ -50,6 +52,7 @@ from django.views.generic import (
|
|||
View,
|
||||
)
|
||||
from django.views.generic.detail import SingleObjectMixin
|
||||
from django.views.static import serve
|
||||
from jsonschema import ValidationError, validate, validators
|
||||
|
||||
from passerelle.base.models import BaseResource, ResourceLog
|
||||
|
@ -629,3 +632,18 @@ class GenericExportConnectorView(GenericConnectorMixin, DetailView):
|
|||
)
|
||||
json.dump({'resources': [self.get_object().export_json()]}, response, indent=2)
|
||||
return response
|
||||
|
||||
|
||||
class MediaView(View):
|
||||
def get(self, request, path, *args, **kwargs):
|
||||
document_root = DefaultStorage().location
|
||||
filename = DefaultStorage().path(path)
|
||||
filename = os.path.realpath(filename)
|
||||
if (
|
||||
not os.path.isabs(filename)
|
||||
or not filename.startswith(document_root)
|
||||
or not os.path.exists(filename)
|
||||
or not os.path.isfile(filename)
|
||||
):
|
||||
raise Http404()
|
||||
return serve(request, path, document_root=document_root, show_indexes=False)
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
# passerelle - uniform access to multiple data sources and services
|
||||
# Copyright (C) 2023 Entr'ouvert
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Affero General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
import pytest
|
||||
from django.core.files.base import ContentFile
|
||||
|
||||
from passerelle.apps.pdf.models import Resource
|
||||
from tests.test_manager import login
|
||||
from tests.utils import setup_access_rights
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def pdf(db):
|
||||
return setup_access_rights(Resource.objects.create(slug='test', title='test', description='test'))
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def cerfa_content():
|
||||
with open('tests/data/cerfa_10072-02.pdf', 'rb') as fd:
|
||||
return fd.read()
|
||||
|
||||
|
||||
def test_media(app, admin_user, simple_user, pdf, cerfa_content):
|
||||
pdf.fill_form_file.save('form.pdf', ContentFile(cerfa_content))
|
||||
|
||||
# refuse anonymous or simple user
|
||||
resp = app.get('/media/pdf/test/form.pdf', status=302)
|
||||
assert resp.location == '/login/?next=/media/pdf/test/form.pdf'
|
||||
|
||||
app = login(app, username='user', password='user')
|
||||
resp = app.get('/media/pdf/test/form.pdf', status=403)
|
||||
|
||||
# allow manager access
|
||||
app = login(app, username='admin', password='admin')
|
||||
resp = app.get('/media/pdf/test/form.pdf')
|
||||
assert resp.content.startswith(b'%PDF')
|
||||
assert resp.headers['content-type'] == 'application/pdf'
|
||||
|
||||
# bad requests: 404 or 400
|
||||
resp = app.get('/media/pdf/plop/there-is-not-file-here.pdf', status=404)
|
||||
resp = app.get('/media/pdf/bad-slug/form.pdf', status=404)
|
||||
resp = app.get('/media/pdf/', status=404)
|
||||
resp = app.get('/media/pdf', status=404)
|
||||
resp = app.get('/media/', status=404)
|
||||
resp = app.get('/media/../etc/passwd', status=400)
|
||||
resp = app.get('/media/../../../../../../../../etc/passwd', status=400)
|
Loading…
Reference in New Issue