ssummer3
2/12/2013 - 7:00 PM

Using STS (Security Token Service) to grant access for a federated user to a s3 bucket prefix

Using STS (Security Token Service) to grant access for a federated user to a s3 bucket prefix

Steps to setting up crap in the AWS console

1. create a bucket that will house the user uploads
2. create an IAM group and attach the permissions policy below. 
   replace "XXX-user-uploads" with the name of the bucket above
3. create an IAM user and add it to the group. these are the access 
   key and secret access key that you use when creating the federated user
AWS_ACCESS_KEY_ID = "xxx"
AWS_SECRET_ACCESS_KEY = "xxx"

AWS_S3_USER_UPLOAD_ACCESS_KEY_ID = "xxx"
AWS_S3_USER_UPLOAD_SECRET_ACCESS_KEY = "xxx"
AWS_S3_USER_UPLOAD_BUCKET = "xxx-user-uploads"
AWS_S3_USER_UPLOAD_TOKEN_DURATION = 60*15 # 15 minutes in seconds

AWS_S3_USER_UPLOAD_POLICY = """{
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "s3:GetObject",
                "s3:DeleteObject"
            ],
            "Resource": "arn:aws:s3:::%%BUCKET_NAME%%/%%USERNAME%%/*"
        },
        {
            "Effect": "Allow",
            "Action": "s3:ListBucket",
            "Resource": "arn:aws:s3:::%%BUCKET_NAME%%",
            "Condition": {
                "StringLike": {
                    "s3:prefix": "%%USERNAME%%/"
                }
            }
        },
        {
            "Effect": "Deny",
            "Action": [
                "sts:*",
                "iam:*",
                "sdb:*"
            ],
            "Resource": "*"
        }
    ]
}"""

{ "Statement" : [ { "Action" : [ "s3:PutObject",
            "s3:GetObject",
            "s3:DeleteObject"
          ],
        "Effect" : "Allow",
        "Resource" : "arn:aws:s3:::XXX-user-uploads/*"
      },
      { "Action" : [ "s3:ListBucket" ],
        "Effect" : "Allow",
        "Resource" : "arn:aws:s3:::XXX-user-uploads"
      },
      { "Action" : "sts:GetFederationToken",
        "Effect" : "Allow",
        "Resource" : "*"
      },
      { "Action" : "iam:GetUser",
        "Effect" : "Allow",
        "Resource" : "*"
      },
      { "Action" : "sdb:*",
        "Effect" : "Allow",
        "Resource" : "*"
      }
    ] }
import json
import boto
from django.db import models
from django.conf import settings
from django.contrib.auth.models import User


class UploadToken(models.Model):
    user = models.ForeignKey(User)
    created = models.DateTimeField(auto_now_add=True)
    access_key_id = models.CharField(max_length=255)
    secret_access_key = models.CharField(max_length=255)
    session_token =  models.TextField()
    expiration = models.DateTimeField()
    uid = models.CharField(max_length=255)

    def save(self, force_insert=False, force_update=False, using=None):
        if not self.access_key_id:
            # connect as the IAM user
            conn = boto.connect_sts(
                aws_access_key_id=settings.AWS_S3_USER_UPLOAD_ACCESS_KEY_ID,
                aws_secret_access_key=settings.AWS_S3_USER_UPLOAD_SECRET_ACCESS_KEY
            )
            if not conn:
                raise Exception("Could not connect to Amazon STS")
            policy = settings.AWS_S3_USER_UPLOAD_POLICY.replace("%%BUCKET_NAME%%", settings.AWS_S3_USER_UPLOAD_BUCKET)
            policy = policy.replace("%%USERNAME%%", self.user.username.encode('utf8'))

            fed_token = conn.get_federation_token(
                self.user.username.encode('utf8'),
                settings.AWS_S3_USER_UPLOAD_TOKEN_DURATION,
                policy
            )
            if not fed_token:
                raise Exception("Could not get federation token for user")
            self.access_key_id = fed_token.credentials.access_key
            self.secret_access_key = fed_token.credentials.secret_key
            self.session_token = fed_token.credentials.session_token
            self.expiration = fed_token.credentials.expiration
            self.uid = fed_token.federated_user_id

        super(UploadToken, self).save(force_insert=force_insert, force_update=force_update, using=using)

    def __unicode__(self):
        return 'UploadToken for {}'.format(self.user.username)