@knasan: Ad 1. Gegenfrage, gibt's da was uneleganteres?
Aber vielleicht erst einmal zu vorhergehenden Problemen. Das fängt mit der Route an, der reguläre Ausdruck ist kaputt, der erlaubt zum Beispiel das hier als Route:
Code: Alles auswählen
"media/something/really anything goes here 🐁/../../../etc/hosts"
"media" ist fest, bei "something" ist man auf Zeichen der ”word”-Klasse beschränkt, aber danach kann dann wirklich *alles* kommen.
Die Urls für diese Route müssen auch nicht mit "media/" anfangen, denn auch das hier geht:
Code: Alles auswählen
"../../really anything goes here 📭/something_a/something_b"
Einzige Einschränkung: `something_a` und `something_b` müssen aus Zeichen der ”word”-Klasse bestehen. Davor kann *alles* stehen.
In keiner der beiden Varianten lässt sich am Ende allerdings eine Dateiendung mit einem Punkt angeben. Das geht dann also nur für Dateien die keine Dateiendung haben.
Reguläre Ausdrücke sind mächtig, aber auch kompliziert und damit fehleranfällig. `path()` würde ich in der Regel vorziehen. Mir ist nicht so ganz klar was Du da genau mit dem regulären Ausdruck erreichen wolltest, aber das wäre wohl so etwas in diese Richtung:
Code: Alles auswählen
path("media/<filename>", views.listfiles, name="listfiles"))
...
def listfiles(request, filename):
...
Bei dem Code der dann in der `listfiles()` bei Dir folgt sind die Pfadoperationen nicht wirklich sicher. Auch wenn der reguläre Ausdruck dich wahrscheinlich schützt, hast Du ja gesehen wie leicht man da Fehler machen kann. Darum würde ich auch danach immer Sicherheitsmassnahmen ergreifen die verhindern das bei absichtlich oder unabsichtlich eingegebenen Daten böse Sachen passieren können, wie beispielsweise jemand der versucht mit "..", "../..", und so weiter über das Wurzelverzeichnis hinaus zu kommen. Aus dem gezeigten Code wird beispielsweise nicht ersichtlich ob Benutzernamen so etwas erlauben, dass man aus einer Kombination aus Benutzernamen ("../../etc/") und URL die mit "/hosts" endet nicht vielleicht doch an die ``/etc/hosts`` auf dem Server heran kommen kann.
Letztlich würde ich das aber sowieso versuchen zu vermeiden selbst zu schreiben. Django bietet da beispielsweise schon eine Abstraktion auf Dateisysteme, so dass man notfalls auch virtuelle Dateisysteme verwenden kann wo die Dateien in Datenbanken gespeichert werden, oder bei Cloudanbietern, oder…
Django selbst bietet da bereits ein `FileSystemStorage` an. Das kümmert sich unter anderem um ein sicheres erstellen eines Pfades, also so das man nicht über das Wurzelverzeichnis heraus kommen kann.
Ungetestet:
Code: Alles auswählen
def download_file(request, filename):
if not request.user.is_authenticated():
return HttpResponseForbidden("<h1>User is not logged in</h1>")
try:
file = default_storage.open(os.path.join(str(request.user), filename))
except FileNotFoundError:
return HttpResponseNotFound("<h1>File not found</h1>")
else:
return FileResponse(file)
Benutzer-ID ist hier eventuell sinnvoller als Benutzername, zumindest wenn das System Änderung des Nutzernamens erlauben sollte.
Dann noch mal Ad 1.: ``.split("/")[1:][0]`` ist etwas umständlich für ``.split("/")[1]``:
Code: Alles auswählen
In [43]: "foo/bar/baz/spam".split("/")
Out[43]: ['foo', 'bar', 'baz', 'spam']
In [44]: "foo/bar/baz/spam".split("/")[1:]
Out[44]: ['bar', 'baz', 'spam']
In [45]: "foo/bar/baz/spam".split("/")[1:][0]
Out[45]: 'bar'
In [46]: "foo/bar/baz/spam".split("/")[1]
Out[46]: 'bar'
Wobei man da bei `split()` auch noch das zweite Argument nutzen kann um unnötige Elemente zu vermeiden:
Code: Alles auswählen
In [47]: "foo/bar/baz/spam".split("/", 2)
Out[47]: ['foo', 'bar', 'baz/spam']
In [48]: "foo/bar/baz/spam".split("/", 2)[1]
Out[48]: 'bar'
Ad 2. MVC? View? Welches MVC? Es gibt nicht *das* MVC. Jedes Rahmenwerk hat offenbar eine eigene Meinung dazu wie MVC auszusehen hat, von einzelnen Entwicklern mal ganz abgesehen. Das ist eine nette Theorie die in der Praxis kein wirkliches Entwurfsmuster ist. Denn Entwurfsmuster impliziert ja, dass man ein Muster gefunden und dem einen Namen gegeben hat, damit alle das gleiche darunter verstehen. Das ist bei MVC so ganz und gar nicht der Fall.
Wie Django das sieht steht in der FAQ:
https://docs.djangoproject.com/en/2.2/f ... dard-names
Die nennen das auch lieber MTV was sie machen — Model, Template, View.