[Fixed]-Amazon s3 and django – Allow only the users from my website and not the anonymous users

7👍

When loading the files directly you can use Amazon’s Query String Authentication where you have to include a signature into the URL to verify that you are allowed to get the files.
Your app can use boto’s Key.generate_url method to create such an url. You should also add an expiry time after which the link will not be valid anymore.

EDIT: A more detailled description about how to set this up using boto,,,

3👍

Since boto3 is out, we can use:
https://github.com/boto/boto3/blob/develop/docs/source/guide/s3-presigned-urls.rst

import boto3
s3_client = boto3.client('s3')
try:
    response = s3_client.generate_presigned_url(ClientMethod=client_method_name,
                                                Params=method_parameters,
                                                ExpiresIn=expiration,
                                                HttpMethod=http_method)
except ClientError as e:
    logging.error(e)
    return None
# The response contains the presigned URL
return response

We can also use similar method for uploading objects in the same ref link.

2👍

So as George said, you need to take 2 steps to make this work, although instead of granting access to ec2 instances (never used that method) i recommend just granting rights to your specific keys (have used this method extensively)

  1. Allow full control to the bucket resources to the user you wish.

Here are the amazon on how permissions work. They are not trivial, so you need to understand who the principal account is, and whether you’re using its keys or the keys for an IAM user. Regardless, your keys should have full access to the resources (Hope it’s your bucket, not someone else’s, cuz then things a little more complicated)
http://docs.aws.amazon.com/AmazonS3/latest/dev/s3-access-control.html

  1. Implement business logic to:
    • allow your app users to create S3 files
    • allow users who are following user X to view/ download files created by user X
    • allow only user X to delete files that he created

For creating the resources:
– if the backend you provided works, and you’ve configured your S3 rights well, you shouldn’t have to do anything. However, to check that your keys are not an issue, do this:

from boto.s3.connection import S3Connection as s3c
connection = s3c('your-aws-access-key', 'your-aws-secret-key')
bucket = connection.get_bucket('your-bucket-name')

new_key = bucket.new_key('your-key-name')  #meaning the name of the file
# https://github.com/boto/boto/blob/develop/boto/s3/bucket.py#L611

new_key.send_file(file_object)  #this will upload the file
# https://github.com/boto/boto/blob/develop/boto/s3/key.py#L709

If everything works well, you should be able to see your file in the S3 browser – if not, you’ll have to get back to the documentation about S3 access rights.

To only allow users who are following user X have access to the files that user uploaded:
Your models.py file doesn’t say how you’ve implemented the follow logic. I can’t tell who is following who. Do you have your own custom User model? Do you use Django’s but with an extension? Do you have your own model which links users? Share more information about how one user ‘follows’ another, and then I could even share code fragments.

To allow user X to delete its own files:
I’m guessing that the Status.user field will already contain a reference to the user that created the image. If so: put this in your view:

def picture_deletion_view(request, status_identifier ...):
    try:
        status = Status.objects.filter(id_or_some_identifier=status_identifier)
    except Status.DoesNotExist:
        return SomeHttpResponse()

    if request.user.id == status.user.id:
        # you can delete the picture and redirect... to somewhere
    else:
        # you can't delete! redirect...or something

0👍

This is logic which is handled by the view and template.
In the template for example you can have a block like this {% if user.is_authenticated %} with a uploadform and in the view you can check also if the user in authenticated and only then load your stuff to s3

👤t_io

-1👍

so i can’t speak to the aws.s3 piece, but for django, the best way to limit who has access to a view is to require login through django’s login_required decorator:

from django.contrib.auth.decorators import login_required

@login_required
def my_view(request):
    ...

or, if you want to expose all of the view, but not parts of the page (links, etc), you could put that information into the template, as @t_io said, but the django docs strongly recommend against putting too much processing logic in the templates, as it slows down the site. it would be better to put that information in the view:

@login_required
def image_view(request):
    user = request.user

    # this list has the user's own images
    mine = []
    for status in user.status_set.all():
        mine.append(status.image)

    # this list has the images the user can see (relationship-based)
    following = []
    friends = []

    # you can get the person from the user
    person = user.person

    for status in person.get_friends().all():
        friends.append(status.image)

    for status in person.get_following().all():
        following.append(status.image)

    ctx = dict(user=request.user, friends=friends, following=following, mine=mine)
    return render("template.html", ctx)

and in the template, you could walk the lists

{% for img in mine %}
    <li><a href=...></a></li>
{% endfor %}

{% for img in following %}
    <li><a href=...></a></li>
{% endfor %}

…you get the idea

to prevent people from just navigating directly to the media url, you could use sendfile (python library), or the apache/nginx equivalent. so you’d need another view:

import sendfile
from django.conf import settings
@login_required
def handle_media(request, path=None):
    try:
        requested_file = os.path.join(settings.MEDIA_ROOT, path)
        return sendfile.sendfile(request, requested_file)
    except:
        pass

    return HttpResponseNotFound('<h1>Page not found {}</h1>'.format(path))

and then you’d need an additional url (urls.py):

urlpatterns = patterns('',
    url(r'/media/(?P<path>[a-zA-Z0-9_- /]+)$', views.handle_media),
)

see django-sendfile for use on sendfile

👤j-mo

Leave a comment