Showing posts with label AWS. Show all posts
Showing posts with label AWS. Show all posts

AWS : how to play a video file from s3 bucket in browser

URI

all you have to do is to sign a request like:

https://s3.amazonaws.com/yourbucket/key?
response-content-disposition=inline&
X-Amz-Algorithm=AWS4-HMAC-SHA256&
X-Amz-Credential=AKIAJWWH7EGSUBWE34IQ/20181228/us-east-1/s3/aws4_request&
X-Amz-Date=20181228T074135Z&
X-Amz-Expires=86400&X-Amz-SignedHeaders=host&
X-Amz-Signature=b3143f8442812e176fd61838813a33f53cfd7e198e1c6e68fab5d03d4a642403
 
and set it as src attribue to a video element.

Player component

import React, { Component } from 'react';
import PropTypes from 'prop-types';

const AWS_SERVICE = 's3';
const crypto = require('crypto');

Date.prototype.toYMDString = function() {
    let year = this.getFullYear().toString();
    let month = this.getUTCMonth() + 1;
    if (month < 10)
      month = "0" + month;
    let day = this.getUTCDate();
    if (day < 10)
      day = "0" + day;
    return String(year) + String(month) 
           + String(day);
}

Date.prototype.toTZString = function() {
    let hour = this.getUTCHours();
    if (hour < 10)
      hour = "0" + hour;
    let minute = this.getUTCMinutes();
    if (minute < 10)
      minute = "0" + minute;
    let second = this.getUTCSeconds();
    if (second < 10)
      second = "0" + second;
    return this.toYMDString() + "T"
      + String(hour) + String(minute)
      + String(second) + "Z";
}

class Player extends Component {
  constructor(props) {
    super(props);
  }

  Hmac(key, string) {
      const hmac = crypto.createHmac('sha256', key);
      hmac.end(string);
      return hmac.read();
  }
  
  Signature(date, region, service, toSign) {
    const { aws_secret_key } = this.props;
      let dateKey = this.Hmac('AWS4' + aws_secret_key, date);
      let dateRegionKey = this.Hmac(dateKey, region);
      let dateRegionServiceKey = this.Hmac(dateRegionKey, service);
      let signingKey = this.Hmac(dateRegionServiceKey, 'aws4_request');
      let signature = this.Hmac(signingKey, toSign).toString('hex');
      return signature;
  }
  
  stringToSign(timeStamp, scope, canonicalRequest) {
      var signParts = [];
      signParts.push('AWS4-HMAC-SHA256');
      signParts.push(timeStamp);
      signParts.push(scope);
      signParts.push(crypto.createHash('sha256').update(canonicalRequest).digest(
  'hex'));
      var result = signParts.join('\n');
      console.log('string to sign');
      console.log(result);
      return result;
  };
  
  canonicalRequest(method, uri, queryString, headers, signedHeaders, hashedPayload) {
    var canonicalParts = [];
    canonicalParts.push(method);
    canonicalParts.push(uri);
    canonicalParts.push(queryString);
    canonicalParts.push(headers);
    canonicalParts.push(signedHeaders);
    canonicalParts.push(hashedPayload);
    var result = canonicalParts.join('\n');
    console.log('canonical request');
    console.log(result);
    return result;
  }

  render() {
    const { aws_key, aws_region, bucket, key } = this.props;
    let uri = "/" + bucket + "/" + key;
    let time_stamp = new Date();
    let tz_time_stamp = time_stamp.toTZString();
    let ymd_time_stamp = time_stamp.toYMDString();
    let scope = ymd_time_stamp + "/" + aws_region + "/" + AWS_SERVICE + "/aws4_request";
    let queryString = "X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=" + encodeURIComponent(aws_key + "/" + scope) + "&X-Amz-Date=" + tz_time_stamp + "&X-Amz-Expires=86400&X-Amz-SignedHeaders=host&response-content-disposition=inline";
    let headers = "host:s3.amazonaws.com";
    let signedHeaders = "";
    let hashedPayload = "host\nUNSIGNED-PAYLOAD";
    let request = this.canonicalRequest('GET', uri, queryString, headers, signedHeaders, hashedPayload);
    let signature = this.Signature(ymd_time_stamp, aws_region, AWS_SERVICE, this.stringToSign(tz_time_stamp, scope, request));
    let url = 'https://s3.amazonaws.com' + uri + '?response-content-disposition=inline&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=' + aws_key + "/" + scope + '&X-Amz-Date=' + tz_time_stamp + '&X-Amz-Expires=86400&X-Amz-SignedHeaders=host&X-Amz-Signature=' + signature;
    console.log(url);
    return (
      <video src={url}
        autoPlay playsInline controls
        style={{width:320,height:240}}>
      </video>
    )
  }
}

Player.propTypes = {
  aws_key: PropTypes.string.isRequired,
  aws_secret_key: PropTypes.string.isRequired,
  aws_region: PropTypes.string.isRequired,
  bucket: PropTypes.string.isRequired,
  key: PropTypes.string.isRequired
};

export default Player;

Usage

    return (
        <Player aws_key={aws_key} bucket={bucket} aws_secret_key={aws_secret_key}
          key={key} aws_region={aws_region}>
        </Player>
    );

example

AWS: how to upload file to your s3 bucket via EvaporateJS in browser

Evaporate and config s3 bucket and IAM user

EvaporateJS
Config AWS s3 bucket
Managing Access Permissions to Your Amazon S3 Resources
User Access Key
In summary, create a s3 bucket, set up CORS settings and bucket policy.
 <CORSConfiguration>
     <CORSRule>
         <AllowedOrigin>https://*.yourdomain.com</AllowedOrigin>
         <AllowedOrigin>http://*.yourdomain.com</AllowedOrigin>
         <AllowedMethod>PUT</AllowedMethod>
         <AllowedMethod>POST</AllowedMethod>
         <AllowedMethod>DELETE</AllowedMethod>
         <AllowedMethod>GET</AllowedMethod>
         <ExposeHeader>ETag</ExposeHeader>
         <AllowedHeader>*</AllowedHeader>
     </CORSRule>
 </CORSConfiguration>
 
{
    "Version": "2012-10-17",
    "Id": "Policy145337ddwd",
    "Statement": [
        {
            "Sid": "",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::6681765859115:user/me"
            },
            "Action": [
                "s3:AbortMultipartUpload",
                "s3:ListMultipartUploadParts",
                "s3:GetObject",
                "s3:PutObject"
            ],
            "Resource": "arn:aws:s3:::mybucket/*"
        }
    ]
} 

Main codes

an example app created via 'create-reat-app' tool.
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';

const Evaporate = require('evaporate');
const crypto = require('crypto');

class App extends Component {
  onFileSelected(files) {
    var config = {
      signerUrl: 'YOURSIGNEDURL',
      aws_key: 'YOURAWSKEY',
      bucket: 'YOURBUCKETNAME',
      cloudfront: true,
      computeContentMd5: true,
      cryptoMd5Method: function (data) {
        return crypto.createHash('md5').update(Buffer.from(data)).digest('base64');  
      },
      cryptoHexEncodedHash256: function (data) {
        return crypto.createHash('sha256').update(Buffer.from(data)).digest('hex');
      }
    };
    return Evaporate.create(config)
      .then(function (evaporate) {
         var addConfig = {
           name: files[0].name,
           file: files[0],
           progress: function (progressValue) {
             console.log('Progress', progressValue);
           },
           complete: function (_xhr, awsKey) {
             console.log('Complete!');
           },
         };
         var overrides = {
            bucket: 'recordingsfortalkmeup'
         };
         evaporate.add(addConfig, overrides)
           .then(function (awsObjectKey) {
                console.log('File successfully uploaded to:', awsObjectKey);
           },
           function (reason) {
             console.log('File did not upload sucessfully:', reason);
           });
      });
  }
  render() {
    return (
      <div className="App">
        <input type='file' onChange={(e) => this.onFileSelected(e.target.files)
 }/>
      </div>
    );
  }
}

export default App;

Signer URL

Create a lambda function to do the signature process.
Signing AWS Requests with Signature Version 4
'use strict';
console.log('Loading upload signature function');

const crypto = require("crypto");

function hmac(key, string){
    const hmac = crypto.createHmac('sha256', key);
    hmac.end(string);
    return hmac.read();
}

exports.handler = function(event, context, callback) {
    console.log(JSON.stringify(event));
    let to_sign = event.queryStringParameters.to_sign;
    let timestamp = event.queryStringParameters.datetime.substr(0, 8);
    console.log(to_sign);    
    console.log('date:', timestamp);
    console.log('aws secret key:', process.env.AWS_SECRET);
    console.log('region:', process.env.AWS_REGION);
    console.log('service:s3');
    let dateKey = hmac('AWS4' + process.env.AWS_SECRET, timestamp);
    let dateRegionKey = hmac(dateKey, process.env.AWS_REGION);
    let dateRegionServiceKey = hmac(dateRegionKey, 's3');
    let signingKey = hmac(dateRegionServiceKey, 'aws4_request');
    let signature = hmac(signingKey, to_sign).toString('hex');
    console.log('Created signature "' + signature + '" from ' + to_sign);
    var response = {
        statusCode: 200,
        headers: {
            "Access-Control-Allow-Origin" : "*",
            "Content-Type": "text/html"
        },
        body: signature
    };
    callback(null, response);
};
 
Build an rest API bind with this Lambda function and deploy it, then you will get an invoke url which will be your signer url.
Build an API Gateway API with Lambda Integration
APIGATEWAY

Test

upload
Everything is ok, you will find new uploaded file in your s3 bucket.
s3bucket

Download file from s3 bucket

AWS : how to get cognito user attributes in Lambda/cloud logic

Lambda function(js)

const AWS = require('aws-sdk');
const cognito = new AWS.CognitoIdentityServiceProvider();

function getUser(Username) {
    return new Promise((resolve, reject) => {
        cognito.adminGetUser({
            UserPoolId: process.env.COGNITO_USER_POOL_ID,
            Username: Username
        }, (err, data) => {
            if (err)
                reject(err.stack);
            else
                resolve(data.UserAttributes[2].Value);
        });
    });
}

async function getEmail(Username) {
  return await getUser(Username);
}

exports.handler = async (event) => {
    console.log(await getEmail('abcdef'));
    console.log(await getEmail('hijklmn'));
    console.log('done');
    return 'ok';
};
set Execution role with correct policy as below descripted.
set an environment 'COGNITO_USER_POOL_ID' with the user pool id.

Role policy

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": "cognito-idp:AdminGetUser",
            "Resource": "your user pool arn"
        }
    ]
}
userpool

Eexcution Result

Response:
"ok"

Request ID:
"a327a22f-0363-11e9-a897-6f0106540a1d"

Function Logs:
START RequestId: a327a22f-0363-11e9-a897-6f0106540a1d Version: $LATEST
2018-12-19T07:56:49.880Z    a327a22f-0363-11e9-a897-6f0106540a1d    errong.leng
@gmail.com
2018-12-19T07:56:50.056Z    a327a22f-0363-11e9-a897-6f0106540a1d    errong.leng
@hotmail.com
2018-12-19T07:56:50.056Z    a327a22f-0363-11e9-a897-6f0106540a1d    done
END RequestId: a327a22f-0363-11e9-a897-6f0106540a1d
REPORT RequestId: a327a22f-0363-11e9-a897-6f0106540a1d  Duration: 1149.09 ms
 Billed Duration: 1200 ms    Memory Size: 128 MB Max Memory Used: 30 MB

Refers

AWS.CognitoIdentityServiceProvider.adminGetUser

node.js download file from aws s3 bucket via http request with AWS Signature Version 4

Main codes

function getYMD(d) {
    let r = d.getFullYear().toString();
    let m = d.getUTCMonth() + 1;
    if (m < 10)
      m = "0" + m.toString();
    else
      m = m.toString();
    r = r + m;
    let day = d.getUTCDate();
    if (day < 10)
      day = "0" + day.toString();
    else
      day = day.toString();
    r = r + day;
    return r;
}

function getTZ(d) {
    let r = d.getFullYear().toString();
    let m = d.getUTCMonth() + 1;
    if (m < 10)
      m = "0" + m.toString();
    else
      m = m.toString();
    r = r + m;
    let day = d.getUTCDate();
    if (day < 10)
      day = "0" + day.toString();
    else
      day = day.toString();
    r = r + day + "T";
    let h = d.getUTCHours();
    if (h < 10)
      h = "0" + h.toString();
    else
      h = h.toString();
    r = r + h;
    let min = d.getUTCMinutes();
    if (min < 10)
      min = "0" + min.toString();
    else
      min = min.toString();
    r = r + min;
    let s = d.getUTCSeconds();
    if (s < 10)
      s = "0" + s.toString();
    else
      s = s.toString();
    r = r + s;
    r = r + "Z";
    return r;
}

const crypto = require('crypto');
function Hmac(key, string){
    const hmac = crypto.createHmac('sha256', key);
    hmac.end(string);
    return hmac.read();
}

function Signature(date, region, service, toSign) {
    let dateKey = Hmac('AWS4' + AWS_SECRETACCESS_KEY, date);
    let dateRegionKey = Hmac(dateKey, region);
    let dateRegionServiceKey = Hmac(dateRegionKey, service);
    let signingKey = Hmac(dateRegionServiceKey, 'aws4_request');
    let signature = Hmac(signingKey, toSign).toString('hex');
    return signature;
}

function stringToSign(timeStamp, scope, canonicalRequest) {
    var signParts = [];
    signParts.push('AWS4-HMAC-SHA256');
    signParts.push(timeStamp);
    signParts.push(scope);
    signParts.push(crypto.createHash('sha256').update(canonicalRequest).digest(
'hex'));
    var result = signParts.join('\n');
    console.log('string to sign');
    console.log(result);
    return result;
};

function canonicalRequest(method, uri, queryString, headers, signedHeaders, has
hedPayload) {
  var canonicalParts = [];
  canonicalParts.push(method);
  canonicalParts.push(uri);
  canonicalParts.push(queryString);
  canonicalParts.push(headers);
  canonicalParts.push(signedHeaders);
  canonicalParts.push(hashedPayload);
  var result = canonicalParts.join('\n');
  console.log('canonical request');
  console.log(result);
  return result;
}

function download(filepath) {
    let filename = filepath;
    let position = filename.lastIndexOf('/');
    if (position != -1) {
      filename = filename.substr(position+1);
    }
    let d = new Date();
    let dtz = getTZ(d);
    let dymd = getYMD(d);
    let scope = dymd + '/' + AWS_REGION + '/' + AWS_SERVICE + '/aws4_request';
    let uri = filepath;
    let queryString = "X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=" + en
codeURIComponent(AWS_KEY + "/" + scope) + "&X-Amz-Date=" + dtz + "&X-Amz-Expire
s=400&X-Amz-SignedHeaders=host&response-content-disposition=attachment%3Bfilena
me%3D" + filename;
    let headers = "host:s3.amazonaws.com";
    let signedHeaders = "";
    let hashedPayload = "host\nUNSIGNED-PAYLOAD";
    let request = canonicalRequest('GET', uri, queryString, headers, signedHead
ers, hashedPayload);
    let signature = Signature(dymd, AWS_REGION, AWS_SERVICE, stringToSign(dtz,
scope, request));
    let url = 'https://s3.amazonaws.com' + filepath + '?response-content-dispos
ition=attachment;filename=' + filename + '&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-A
mz-Credential=' + AWS_KEY + "/" + scope + '&X-Amz-Date=' + dtz + '&X-Amz-Expire
s=400&X-Amz-SignedHeaders=host&X-Amz-Signature=' + signature;
    console.log(url);
    var link = document.createElement('a');
    link.innerHTML = 'download';
    link.href = url;
    document.body.appendChild(link);
}

Usage

const AWS_KEY = 'your aws key';
const AWS_SECRETACCESS_KEY = 'your aws secret access key';
const AWS_REGION = 'your bucket region'; // default us-east-1
const AWS_SERVICE = 's3';

download('/yourbucket/objectkey');

examle

download

Refers

GET Object
Signing AWS Requests with Signature Version 4
Authenticating Requests: Using Query Parameters (AWS Signature Version 4)
Key point:
?response-content-disposition=attachment;filename=objectname
Using this header, browser will open an save dialog once the down load url is correct signed.
You will get a url link like:
https://s3.amazonaws.com/bucket/objectkey?response-content-disposition=attachment;filename=objectname&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAJWWH7EGSUBWE34IQ/20181218/us-east-1/s3/aws4_request&X-Amz-Date=20181218T053905Z&X-Amz-Expires=400&X-Amz-SignedHeaders=host&X-Amz-Signature=71d82d4a99338d777bfa3517315d34cad94619a9ca891222808e16dd02de7835

fixed: embedded-redis: Unable to run on macOS Sonoma

Issue you might see below error while trying to run embedded-redis for your testing on your macOS after you upgrade to Sonoma. java.la...