Как аутентифицировать бессерверный веб-запрос с помощью AWS Web API и Lambda?

Немного справочной информации,

Я создал интерактивный веб-сайт, на котором пользователи могут загружать изображения в S3. Я построил его таким образом, что загрузка изображения идет прямо из браузера в AWS S3 с использованием подписанного запроса (бэкэнд python django).

Теперь проблема в том, что пользователи хотят иметь возможность вращать изображение. I Точно так же я хотел бы, чтобы это было настроено так, чтобы запрос пользователя поступал прямо из браузера. Я создал функцию AWS Lambda и прикрепил ее к веб-API, который будет принимать запросы POST. Я тестировал, и у меня наконец-то все заработало. Функция принимает 2 входа, key и rotate_direction, которые передаются как переменные POST в веб-API. Они входят в функцию python в переменную event. Вот простая лямбда-функция:

from __future__ import print_function
import boto3
import os
import sys
import uuid
from PIL import Image

s3_client = boto3.client('s3')

def rotate_image(image_path, upload_path, rotate_direction):
    with Image.open(image_path) as image:
        if rotate_direction == "right":
            image.rotate(-90).save(upload_path)
        else:
            image.rotate(90).save(upload_path)

def handler(event, context):
    bucket = 'the-s3-bucket-name'
    key = event['key']
    rotate_direction = event['rotate_direction']
    download_path = '/tmp/{}{}'.format(uuid.uuid4(), key)
    upload_path = '/tmp/rotated_small-{}'.format(key)


    s3_client.download_file(bucket, key, download_path)
    rotate_image(download_path, upload_path, rotate_direction)
    s3_client.delete_object(Bucket=bucket, Key=key)
    s3_client.upload_file(upload_path, bucket, key)

    return { 'message':'rotated' }

Все работает. Итак, теперь моя проблема заключается в том, как обеспечить некоторую аутентификацию для этой системы? Информация о владельце каждого изображения находится на веб-сервере django. Хотя все изображения считаются «общедоступными», я хочу обеспечить, чтобы только владелец каждого изображения мог вращать свои собственные изображения.

С этим проектом я отважился выйти на новую территорию, отправляя запросы контента прямо из браузера. Я мог понять, как я могу контролировать доступ, отправляя только POST-запросы с веб-сервера, где я мог подтвердить право собственности на изображения. Возможно ли, чтобы запрос исходил из браузера?


person jeffery_the_wind    schedule 18.02.2017    source источник


Ответы (1)


TL;DR Решение: создайте Cognito Identity Pool, назначьте пользователям политики возможность загружать только файлы с префиксом их Identity ID.

Если я правильно понимаю ваш вопрос, вы хотите настроить способ, чтобы изображение, хранящееся на S3, было доступно для просмотра всем, но редактировалось только пользователем, который его загрузил. Фактически, вы можете подтвердить право собственности на файл, повернуть изображение и загрузить повернутое изображение на S3 в браузере, не используя функцию Lambda.

Шаг 1: Создайте пул пользователей Cognito, чтобы создать пользовательский каталог. Если у вас уже есть система аутентификации входа/регистрации пользователя, вы можете пропустить этот шаг.

Шаг 2. Создайте пул идентификации Cognito, чтобы включить федеративное удостоверение, чтобы ваши пользователи могли получать временные учетные данные AWS из пула удостоверений и использовать их для загрузки файлов на S3, минуя ваш сервер/лямбду.

Шаг 3. При создании пула удостоверений Cognito вы можете определить политику в отношении того, к каким ресурсам S3 разрешен доступ пользователю. Вот пример политики

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "mobileanalytics:PutEvents",
        "cognito-sync:*",
        "cognito-identity:*"
      ],
      "Resource": [
        "*"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "s3:GetObject"
      ],
      "Resource": [
        "arn:aws:s3:::YOUR_S3_UPLOADS_BUCKET_NAME/*"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "s3:PutObject"
      ],
      "Resource": [
        "arn:aws:s3:::YOUR_S3_UPLOADS_BUCKET_NAME/${cognito-identity.amazonaws.com:sub}*"
      ]
    }
  ]
}

Обратите внимание, что второй блок назначает «S3:GetObject» всем файлам в корзине S3; а третий блок присваивает "S3:PutObject" ТОЛЬКО ФАЙЛАМ с префиксом Cognito Identity ID пользователя.

Шаг 4. Во внешнем JS получите временные учетные данные из пула удостоверений Cognito.

export function getAwsCredentials(userToken) {
  const authenticator = `cognito-idp.${config.cognito.REGION}.amazonaws.com/${config.cognito.USER_POOL_ID}`;

  AWS.config.update({ region: config.cognito.REGION });

  AWS.config.credentials = new AWS.CognitoIdentityCredentials({
    IdentityPoolId: config.cognito.IDENTITY_POOL_ID,
    Logins: {
      [authenticator]: userToken
    }
  });

  return new Promise((resolve, reject) => (
    AWS.config.credentials.get((err) => {
      if (err) {
        reject(err);
        return;
      }

      resolve();
    })
  ));
}

Шаг 5: Загрузите файлы на S3 с учетными данными, добавьте к имени файла префикс идентификатора Cognito Identity ID пользователя.

export async function s3Upload(file, userToken) {
  await getAwsCredentials(userToken);

  const s3 = new AWS.S3({
    params: {
      Bucket: config.s3.BUCKET,
    }
  });
  const filename = `${AWS.config.credentials.identityId}-${Date.now()}-${file.name}`;

  return new Promise((resolve, reject) => (
    s3.putObject({
      Key: filename,
      Body: file,
      ContentType: file.type,
      ACL: 'public-read',
    },
    (error, result) => {
      if (error) {
        reject(error);
        return;
      }

      resolve(`${config.s3.DOMAIN}/${config.s3.BUCKET}/${filename}`);
    })
  ));
}
person Frank    schedule 06.03.2017
comment
Это довольно сильный ответ. Огромное спасибо. Определенно прочитаю учебник и посмотрю, как это интегрировать. Мое приложение фактически использует Firebase для аутентификации пользователя, но для загрузки файла используется предварительно подписанный запрос (boto3.readthedocs.io/en/latest/guide/) с моего сервера и передает его обратно клиенту, прежде чем клиент сможет загрузить изображение. Кажется, с вашей техникой здесь я смогу пропустить этот шаг (без сервера). Также сейчас все изображения не имеют специального префикса, поэтому мне придется немного переработать все это. - person jeffery_the_wind; 07.03.2017
comment
Рад, что ты нашел обходной путь. Да, согласно документу Amazon, вы либо добавляете префикс к имени файла, либо сохраняете изображения в именах папок с префиксом идентификатора пользователя. - person Frank; 08.03.2017
comment
Да, теперь, когда вы это говорите, я понимаю, что именно так они обрабатывали имена файлов в других проектах, над которыми я работал. Я решил, что мне это не нужно, потому что база данных БД содержит информацию о владении, но теперь я вижу, как может помочь именование со специальным кодом для каждого пользователя. - person jeffery_the_wind; 08.03.2017