Skip to content

Instantly share code, notes, and snippets.

@wizpig64
Created November 27, 2013 04:58
Show Gist options
  • Save wizpig64/7670865 to your computer and use it in GitHub Desktop.
Save wizpig64/7670865 to your computer and use it in GitHub Desktop.
Makes Django storage URLs expire after a set amount of time. Built to be used with nginx's secure_link module, essentially emulating Amazon's S3's similar functionality. Because it's a mixin, you can theoretically use it with any Django storage method, but it will only limit access if there's a properly configured nginx server actually serving t…
from base64 import urlsafe_b64encode
from urlparse import parse_qs, urlsplit, urlunsplit
from urllib import unquote, urlencode
from django.conf import settings
from django.core.files.storage import Storage
from django.core.files.storage import FileSystemStorage
from secret_hash import SecretHash
class SecretHash(SecretHash):
SECRET_KEY = settings.SECRET_KEY
SECRET_LIFESPAN = getattr(settings, 'SECRET_URL_LIFESPAN', 86400) #24h
class SecureStorageMixin(Storage):
"""Append some paramaters to make urls more secure and expire.
Made to support nginx and its http_secure_link_module:
http://wiki.nginx.org/HttpSecureLinkModule
The secure link module is not compiled with nginx by default. If
you're using Ubuntu like me, apt-get install nginx-extras to get it.
Here's how NGINX can be set up if your secret is "SECRET$KEY123":
(NGINX can't natively escape dollar sign literals, so use ${dollar})
geo $dollar {
default "$";
}
server {
...
location /media {
secure_link $arg_key,$arg_exp;
secure_link_md5 SECRET${dollar}KEY123$arg_exp$uri;
# If the hash is incorrect then $secure_link is a null string.
if ($secure_link = "") {
return 403;
}
# The current local time is greater than the specified expiration.
if ($secure_link = "0") {
return 403;
}
...
}
}
"""
def url(self, name):
# Take the URL generated by super and break it up
u = super(SecureStorageMixin, self).url(name)
scheme, netloc, path, query_string, fragment = urlsplit(u)
query_params = parse_qs(query_string)
# Generate the extra parameters and reconstruct the URL.
hash = SecretHash(unquote(path))
query_params['key'] = [hash.key]
query_params['exp'] = [hash.expires]
new_query_string = urlencode(query_params, doseq=True)
return urlunsplit((scheme, netloc, path, new_query_string, fragment))
class SecureFileSystemStorage(SecureStorageMixin, FileSystemStorage): pass
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment