Skip to content

Commit

Permalink
Add requestDate parameter to presignedUrl() and presignedGetObject() (#…
Browse files Browse the repository at this point in the history
…728)

Fixes #724
  • Loading branch information
dustin-H authored and kannappanr committed Dec 4, 2018
1 parent 6489ee2 commit a180e87
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 11 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ script:
- gulp lint

before_install:
- npm i -g yarn
- npm i -g yarn@1.9.4

install:
- yarn
13 changes: 8 additions & 5 deletions docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -703,7 +703,7 @@ minioClient.removeIncompleteUpload('mybucket', 'photo.jpg', function(err) {
Presigned URLs are generated for temporary download/upload access to private objects.

<a name="presignedUrl"></a>
### presignedUrl(httpMethod, bucketName, objectName, expiry[, reqParams, cb])
### presignedUrl(httpMethod, bucketName, objectName[, expiry, reqParams, requestDate, cb])

Generates a presigned URL for the provided HTTP method, 'httpMethod'. Browsers/Mobile clients may point to this URL to directly download objects even if the bucket is private. This presigned URL can have an associated expiration time in seconds after which the URL is no longer valid. The default value is 7 days.

Expand All @@ -716,8 +716,9 @@ __Parameters__
|---|---|---|
|`bucketName` | _string_ | Name of the bucket. |
|`objectName` | _string_ | Name of the object. |
|`expiry` | _number_ | Expiry time in seconds. Default value is 7 days. |
|`reqParams` | _object_ | request parameters. |
|`expiry` | _number_ | Expiry time in seconds. Default value is 7 days. (optional) |
|`reqParams` | _object_ | request parameters. (optional) |
|`requestDate` | _Date_ | A date object, the url will be issued at. Default value is now. (optional) |
|`callback(err, presignedUrl)` | _function_ | Callback function is called with non `null` err value in case of error. `presignedUrl` will be the URL using which the object can be downloaded using GET request. If no callback is passed, a `Promise` is returned. |


Expand Down Expand Up @@ -748,7 +749,7 @@ minioClient.presignedUrl('GET', 'mybucket', '', 1000, {'prefix': 'data', 'max-ke
```

<a name="presignedGetObject"></a>
### presignedGetObject(bucketName, objectName, expiry[, cb])
### presignedGetObject(bucketName, objectName[, expiry, respHeaders, requestDate, cb])

Generates a presigned URL for HTTP GET operations. Browsers/Mobile clients may point to this URL to directly download objects even if the bucket is private. This presigned URL can have an associated expiration time in seconds after which the URL is no longer valid. The default value is 7 days.

Expand All @@ -761,7 +762,9 @@ __Parameters__
|---|---|---|
|`bucketName` | _string_ | Name of the bucket. |
|`objectName` | _string_ | Name of the object. |
|`expiry` | _number_ | Expiry time in seconds. Default value is 7 days. |
|`expiry` | _number_ | Expiry time in seconds. Default value is 7 days. (optional) |
|`respHeaders` | _object_ | response headers to override (optional) |
|`requestDate` | _Date_ | A date object, the url will be issued at. Default value is now. (optional) |
|`callback(err, presignedUrl)` | _function_ | Callback function is called with non `null` err value in case of error. `presignedUrl` will be the URL using which the object can be downloaded using GET request. If no callback is passed, a `Promise` is returned. |


Expand Down
34 changes: 34 additions & 0 deletions examples/presigned-getobject-request-date.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Minio Javascript Library for Amazon S3 Compatible Cloud Storage, (C) 2015 Minio, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

// Note: YOUR-ACCESSKEYID, YOUR-SECRETACCESSKEY, my-bucketname and my-objectname
// are dummy values, please replace them with original values.

var Minio = require('minio')

var s3Client = new Minio.Client({
endPoint: 's3.amazonaws.com',
accessKey: 'YOUR-ACCESSKEYID',
secretKey: 'YOUR-SECRETACCESSKEY',
useSSL: true // Default is true.
})

// Presigned get object URL for my-objectname at my-bucketname, it expires in 7 days by default.
var requestDate = new Date('2017-01-01');
var presignedUrl = s3Client.presignedGetObject('my-bucketname', 'my-objectname', 1000, {}, requestDate, function(e, presignedUrl) {
if (e) return console.log(e)
console.log(presignedUrl)
})
5 changes: 5 additions & 0 deletions src/main/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,11 @@ export function isArray(arg) {
return Array.isArray(arg)
}

// check if arg is a valid date
export function isValidDate(arg) {
return arg instanceof Date && !isNaN(arg)
}

// Create a Date string with format:
// 'YYYYMMDDTHHmmss' + Z
export function makeDateLong(date) {
Expand Down
20 changes: 15 additions & 5 deletions src/main/minio.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import _ from 'lodash'
import { extractMetadata, prependXAMZMeta, isValidPrefix, isValidEndpoint, isValidBucketName,
isValidPort, isValidObjectName, isAmazonEndpoint, getScope,
uriEscape, uriResourceEscape, isBoolean, isFunction, isNumber,
isString, isObject, isArray, pipesetup,
isString, isObject, isArray, isValidDate, pipesetup,
readableStream, isReadableStream, isVirtualHostStyle,
makeDateLong, promisify } from './helpers.js'

Expand Down Expand Up @@ -1559,29 +1559,38 @@ export class Client {
// * `objectName` _string_: name of the object
// * `expiry` _number_: expiry in seconds (optional, default 7 days)
// * `reqParams` _object_: request parameters (optional)
presignedUrl(method, bucketName, objectName, expires, reqParams, cb) {
// * `requestDate` _Date_: A date object, the url will be issued at (optional)
presignedUrl(method, bucketName, objectName, expires, reqParams, requestDate, cb) {
if (this.anonymous) {
throw new errors.AnonymousRequestError('Presigned ' + method + ' url cannot be generated for anonymous requests')
}
if (isFunction(requestDate)) {
cb = requestDate
requestDate = new Date()
}
if (isFunction(reqParams)) {
cb = reqParams
reqParams = {}
requestDate = new Date()
}
if (isFunction(expires)) {
cb = expires
reqParams = {}
expires = 24 * 60 * 60 * 7 // 7 days in seconds
requestDate = new Date()
}
if (!isNumber(expires)) {
throw new TypeError('expires should be of type "number"')
}
if (!isObject(reqParams)) {
throw new TypeError('reqParams should be of type "object"')
}
if (!isValidDate(requestDate)) {
throw new TypeError('requestDate should be of type "Date" and valid')
}
if (!isFunction(cb)) {
throw new TypeError('callback should be of type "function"')
}
var requestDate = new Date()
var query = querystring.stringify(reqParams)
this.getBucketRegion(bucketName, (e, region) => {
if (e) return cb(e)
Expand Down Expand Up @@ -1610,7 +1619,8 @@ export class Client {
// * `objectName` _string_: name of the object
// * `expiry` _number_: expiry in seconds (optional, default 7 days)
// * `respHeaders` _object_: response headers to override (optional)
presignedGetObject(bucketName, objectName, expires, respHeaders, cb) {
// * `requestDate` _Date_: A date object, the url will be issued at (optional)
presignedGetObject(bucketName, objectName, expires, respHeaders, requestDate, cb) {
if (!isValidBucketName(bucketName)) {
throw new errors.InvalidBucketNameError('Invalid bucket name: ' + bucketName)
}
Expand All @@ -1624,7 +1634,7 @@ export class Client {
throw new TypeError(`response header ${header} should be of type "string"`)
}
})
return this.presignedUrl('GET', bucketName, objectName, expires, respHeaders, cb)
return this.presignedUrl('GET', bucketName, objectName, expires, respHeaders, requestDate, cb)
}

// Generate a presigned URL for PUT. Using this URL, the browser can upload to S3 only with the specified object name.
Expand Down
50 changes: 50 additions & 0 deletions src/test/functional/functional-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -751,6 +751,31 @@ describe('functional tests', function() {
})
})

step(`presignedUrl(httpMethod, bucketName, objectName, expires, cb)_httpMethod:GET, bucketName:${bucketName}, objectName:${_1byteObjectName}, expires:86400, requestDate:StartOfDay_`, done => {
var requestDate = new Date()
requestDate.setHours(0,0,0,0)
client.presignedUrl('GET', bucketName, _1byteObjectName, 86400, requestDate, (e, presignedUrl) => {
if (e) return done(e)
var transport = http
var options = _.pick(url.parse(presignedUrl), ['hostname', 'port', 'path', 'protocol'])
options.method = 'GET'
if (options.protocol === 'https:') transport = https
var request = transport.request(options, (response) => {
if (response.statusCode !== 200) return done(new Error(`error on put : ${response.statusCode}`))
var error = null
response.on('error', e => done(e))
response.on('end', () => done(error))
response.on('data', (data) => {
if (data.toString() !== _1byte.toString()) {
error = new Error('content mismatch')
}
})
})
request.on('error', e => done(e))
request.end()
})
})

step(`presignedGetObject(bucketName, objectName, cb)_bucketName:${bucketName}, objectName:${_1byteObjectName}_`, done => {
client.presignedGetObject(bucketName, _1byteObjectName, (e, presignedUrl) => {
if (e) return done(e)
Expand Down Expand Up @@ -823,6 +848,31 @@ describe('functional tests', function() {
})
})

step(`presignedGetObject(bucketName, objectName, cb)_bucketName:${bucketName}, objectName:${_1byteObjectName}, expires:86400, requestDate:StartOfDay_`, done => {
var requestDate = new Date()
requestDate.setHours(0,0,0,0)
client.presignedGetObject(bucketName, _1byteObjectName, 86400, {}, requestDate, (e, presignedUrl) => {
if (e) return done(e)
var transport = http
var options = _.pick(url.parse(presignedUrl), ['hostname', 'port', 'path', 'protocol'])
options.method = 'GET'
if (options.protocol === 'https:') transport = https
var request = transport.request(options, (response) => {
if (response.statusCode !== 200) return done(new Error(`error on put : ${response.statusCode}`))
var error = null
response.on('error', e => done(e))
response.on('end', () => done(error))
response.on('data', (data) => {
if (data.toString() !== _1byte.toString()) {
error = new Error('content mismatch')
}
})
})
request.on('error', e => done(e))
request.end()
})
})

step('presignedPostPolicy(postPolicy, cb)_postPolicy:expiresin10days_', done => {
var policy = client.newPostPolicy()
policy.setKey(_1byteObjectName)
Expand Down

0 comments on commit a180e87

Please sign in to comment.