Repository: minio/minio-js
Pull request #676 (2018-03-08 07:01:43)

set/getBucketPolicy takes/returns policy JSON file
It was decided to make set/getBucketPolicy a low level API directly dealing with the policy json file.

krishnasrinivas: @ebozduman fixed

@kannappanr will increment the version number after the putObject-metadata changes are in. So we can have only one major version number change.
harshavardhana: > @kannappanr will increment the version number after the putObject-metadata changes are in. So we can have only one major version number change.

Actually, you need to make the change in the PR which breaks the app @krishnasrinivas even accidentally anyone pulling from git shouldn't get the same version. 
harshavardhana: > Actually, you need to make the change in the PR which breaks the package @krishnasrinivas even accidentally anyone pulling from git shouldn't get the same version.

We should do it in this PR IMO
kannappanr: blocked on updating the version
ebozduman: @krishnasrinivas,
I just noticed you did not touch the `set-bucket-policy.js` and `get-bucket-policy.js` example files. They also need to be modified accordingly.
kannappanr: We might have to add unit tests for set and get bucket policy
krishnasrinivas: @kannappanr actually package.json version is already incremented to 5 (by Anis) in one of the previous PRs. We haven't made a release after that. What we can do is take putObject() changes also and make a release 5.0.0

Patch:
diff --git a/docs/API.md b/docs/API.md
index 0794656..d83e990 100644
--- a/docs/API.md
+++ b/docs/API.md
@@ -943,7 +943,7 @@ listener.on('notification', function(record) {
 ```
 
 <a name="getBucketPolicy"></a>
-### getBucketPolicy(bucketName, objectPrefix[, callback])
+### getBucketPolicy(bucketName [, callback])
 
 Get the bucket policy associated with the specified bucket. If `objectPrefix`
 is not empty, the bucket policy will be filtered based on object permissions
@@ -955,45 +955,39 @@ __Parameters__
 | Param  |  Type | Description  |
 |---|---|---|
 | `bucketName`  | _string_  | Name of the bucket |
-| `objectPrefix` | _string_ | Prefix of objects in the bucket with which to filter permissions off of. Use `''` for entire bucket. |
-| `callback(err, policy)`  | _function_  | Callback function is called with non `null` err value in case of error. `policy` will be the string representation of the bucket policy (`minio.Policy.NONE`, `minio.Policy.READONLY`, `minio.Policy.WRITEONLY`, or `minio.Policy.READWRITE`). If no callback is passed, a `Promise` is returned. |
+| `callback(err, policy)`  | _function_  | Callback function is called with non `null` err value in case of error. `policy` is the [bucket policy](https://github.com/minio/minio/blob/master/docs/bucket/policy/README.md). If no callback is passed, a `Promise` is returned. |
 
 
 ```js
-// Retrieve bucket policy of 'my-bucketname' that applies to all objects that
-// start with 'img-'.
-minioClient.getBucketPolicy('my-bucketname', 'img-', function(err, policy) {
+// Retrieve bucket policy of 'my-bucketname'
+minioClient.getBucketPolicy('my-bucketname', function(err, policy) {
   if (err) throw err
 
-  console.log(`Bucket policy: ${policy}`)
+  console.log(`Bucket policy file: ${policy}`)
 })
 ```
 
 <a name="setBucketPolicy"></a>
-### setBucketPolicy(bucketName, objectPrefix, bucketPolicy[, callback])
+### setBucketPolicy(bucketName, bucketPolicy[, callback])
 
-Set the bucket policy associated with the specified bucket. If `objectPrefix`
-is not empty, the bucket policy will only be assigned to objects that fit the
-given prefix.
+Set the bucket policy on the specified bucket. [bucketPolicy](https://github.com/minio/minio/blob/master/docs/bucket/policy/README.md) is detailed here.
 
 __Parameters__
 
 
 | Param  |  Type | Description  |
 |---|---|---|
-| `bucketName`  | _string_  | Name of the bucket |
-| `objectPrefix` | _string_ | Prefix of objects in the bucket to modify permissions of. Use `''` for entire bucket. |
-| `bucketPolicy` | _string_ | The bucket policy. This can be: `minio.Policy.NONE`, `minio.Policy.READONLY`, `minio.Policy.WRITEONLY`, or `minio.Policy.READWRITE`. |
+| `bucketName`  | _string_  | Name of the bucket. |
+| `bucketPolicy` | _string_ | bucket policy. |
 | `callback(err)`  | _function_  | Callback function is called with non `null` err value in case of error. If no callback is passed, a `Promise` is returned. |
 
 
 ```js
-// Set the bucket policy of `my-bucketname` to `readonly` (only allow retrieval),
-// but only for objects that start with 'img-'.
-minioClient.setBucketPolicy('my-bucketname', 'img-', minio.Policy.READONLY, function(err) {
+// Set the bucket policy of `my-bucketname`
+minioClient.setBucketPolicy('my-bucketname', JSON.stringify(policy), function(err) {
   if (err) throw err
 
-  console.log('Set bucket policy to \'readonly\'.')
+  console.log('Bucket policy set')
 })
 ```
 
diff --git a/examples/get-bucket-policy.js b/examples/get-bucket-policy.js
index 1c1639a..3b26fb9 100644
--- a/examples/get-bucket-policy.js
+++ b/examples/get-bucket-policy.js
@@ -26,9 +26,7 @@ var s3Client = new Minio.Client({
 })
 
 // Retrieves the bucket policy and logs it to the console.
-// The second argument is the prefix for objects, leave empty if you don't
-// want to filter based on object-specific permissions.
-s3Client.getBucketPolicy('my-bucketname', '', (err, policy) => {
+s3Client.getBucketPolicy('testbucket', (err, policy) => {
 	if (err) throw err
 
 	console.log(`Bucket policy: ${policy}`)
diff --git a/examples/set-bucket-policy.js b/examples/set-bucket-policy.js
index 5cc7025..96d7f7a 100644
--- a/examples/set-bucket-policy.js
+++ b/examples/set-bucket-policy.js
@@ -25,13 +25,48 @@ var s3Client = new Minio.Client({
   secretKey: 'YOUR-SECRETACCESSKEY'
 })
 
-// Sets the bucket policy to 'readonly'. This means that objects can only be
-// retrieved rather than created, modified, or destroyed in this bucket.
+// Bucket policy - GET requests on "testbucket" bucket will not need authentication.
+var policy = `
+{
+  "Version": "2012-10-17",
+  "Statement": [
+    {
+      "Action": [
+        "s3:GetBucketLocation",
+        "s3:ListBucket"
+      ],
+      "Effect": "Allow",
+      "Principal": {
+        "AWS": [
+          "*"
+        ]
+      },
+      "Resource": [
+        "arn:aws:s3:::testbucket"
+      ],
+      "Sid": ""
+    },
+    {
+      "Action": [
+        "s3:GetObject"
+      ],
+      "Effect": "Allow",
+      "Principal": {
+        "AWS": [
+          "*"
+        ]
+      },
+      "Resource": [
+        "arn:aws:s3:::testbucket/*"
+      ],
+      "Sid": ""
+    }
+  ]
+}
+`
 
-// The second parameter is for filtering based on objects — you can leave this
-// empty if you'd like the permissions to apply to the entire bucket.
-s3Client.setBucketPolicy('my-bucketname', '', Minio.Policy.READONLY, (err) => {
+s3Client.setBucketPolicy('testbucket', policy, (err) => {
 	if (err) throw err
 
-	console.log('Set bucket policy to \'readonly\'.')
+	console.log('Set bucket policy')
 })
diff --git a/src/main/bucket-policy.js b/src/main/bucket-policy.js
deleted file mode 100644
index 16a3cd8..0000000
--- a/src/main/bucket-policy.js
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- * 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.
- */
-
-import _ from 'lodash'
-import * as errors from './errors'
-import { isValidBucketName } from './helpers'
-
-export const Policy = {
-  NONE: 'none',
-  READONLY: 'readonly',
-  WRITEONLY: 'writeonly',
-  READWRITE: 'readwrite'
-}
-
-const resourcePrefix = 'arn:aws:s3:::'
-
-const readActions = {
-  bucket: [ 's3:GetBucketLocation' ],
-  object: [ 's3:GetObject' ]
-}
-
-const writeActions = {
-  bucket: [ 's3:GetBucketLocation', 's3:ListBucketMultipartUploads' ],
-  object: [ 's3:AbortMultipartUpload', 's3:DeleteObject',
-            's3:ListMultipartUploadParts', 's3:PutObject' ]
-}
-
-// Returns the string version of the bucket policy.
-export function parseBucketPolicy(policy, bucketName, objectPrefix) {
-  let statements = policy.Statement
-
-  // If there are no statements, it's none.
-  if (statements.length === 0) return Policy.NONE
-
-  let bucketResource = `${resourcePrefix}${bucketName}`
-  let objectResource = `${resourcePrefix}${bucketName}/${objectPrefix}`
-
-  let actions = {
-    bucket: [],
-    object: []
-  }
-
-  // Loop through the statements and aggregate actions which are allowed.
-  for (let i = 0; i < statements.length; i++) {
-    let statement = statements[i]
-
-    // Normalize the statement, as AWS will drop arrays with length 1.
-    statement = normalizeStatement(statement)
-
-    // Verify the statement before we union-ize the actions.
-    if (!_.some(statement.Principal.AWS, value => value == '*')) continue
-    if (statement.Effect != 'Allow') continue
-
-    // Check for object actions or bucket actions, depending on the resource.
-    if (pertainsTo(statement, objectResource)) {
-      actions.object = _.union(actions.object, statement.Action)
-    } else if (pertainsTo(statement, bucketResource)) {
-      actions.bucket = _.union(actions.bucket, statement.Action)
-    }
-  }
-
-  // Check for permissions.
-  let canRead = false
-  let canWrite = false
-
-  // If it has a subarray inside, there are full permissions to either
-  // read, write, or both.
-  if (isSubArrayOf(actions.bucket, writeActions.bucket) &&
-      isSubArrayOf(actions.object, writeActions.object)) {
-    canWrite = true
-  }
-
-  if (isSubArrayOf(actions.bucket, readActions.bucket) &&
-      isSubArrayOf(actions.object, readActions.object)) {
-    canRead = true
-  }
-
-  if (canRead && canWrite) return Policy.READWRITE
-  else if (canRead) return Policy.READONLY
-  else if (canWrite) return Policy.WRITEONLY
-  else return Policy.NONE
-}
-
-// Generate a statement payload to submit to S3 based on the given policy.
-export function generateBucketPolicy(policy, bucketName, objectPrefix) {
-  if (!isValidBucketName(bucketName)) {
-    throw new errors.InvalidBucketNameError(`Invalid bucket name: ${bucketName}`)
-  }
-  if (!isValidBucketPolicy(policy)) {
-    throw new errors.InvalidBucketPolicyError(`Invalid bucket policy: ${policy}` +
-                                                `(must be 'none', 'readonly', 'writeonly', or 'readwrite')`)
-  }
-
-  // Merge the actions together based on the given policy.
-  let actions = {
-    bucket: [],
-    object: []
-  }
-
-  if (policy == Policy.READONLY || policy == Policy.READWRITE) {
-    // Do read statements.
-    actions.bucket = _.concat(actions.bucket, readActions.bucket)
-    actions.object = _.concat(actions.object, readActions.object)
-  }
-
-  if (policy == Policy.WRITEONLY || policy == Policy.READWRITE) {
-    // Do write statements.
-    actions.bucket = _.concat(actions.bucket, writeActions.bucket)
-    actions.object = _.concat(actions.object, writeActions.object)
-  }
-
-  // Drop any duplicated actions.
-  actions.bucket = _.uniq(actions.bucket)
-  actions.object = _.uniq(actions.object)
-
-  // Form statements from the actions. We'll create three statements:
-  // one for basic bucket permissions, one for basic object permissions,
-  // and finally a special statement for ListBucket, which should be
-  // handled separately.
-  let statements = []
-
-  if (actions.bucket.length > 0) {
-    statements.push(createStatement(actions.bucket, `${resourcePrefix}${bucketName}`))
-  }
-
-  if (actions.object.length > 0) {
-    statements.push(createStatement(actions.object, `${resourcePrefix}${bucketName}/${objectPrefix}*`))
-  }
-
-  // If reading permission is on, add ListBucket.
-  if (policy == Policy.READONLY || policy == Policy.READWRITE) {
-    let listBucketStatement = createStatement([ 's3:ListBucket' ], `${resourcePrefix}${bucketName}`)
-
-    // It has a condition on it if there is a prefix, thus we do it separately.
-    if (objectPrefix !== '') {
-      listBucketStatement.Condition = { StringEquals: { 's3:prefix': objectPrefix } }
-    }
-
-    statements.push(listBucketStatement)
-  }
-
-  // s3 requires a wrapper around the statements.
-  return {
-    'Version': '2012-10-17',
-    'Statement': statements
-  }
-}
-
-export function isValidBucketPolicy(policy) {
-  return policy == Policy.NONE ||
-         policy == Policy.READONLY ||
-         policy == Policy.WRITEONLY ||
-         policy == Policy.READWRITE
-}
-
-// Checks to see if the parent array has all the values in the child array.
-// Take the intersection for both. If the lengths are the same, the contents
-// of the child are inside the parent.
-function isSubArrayOf(parent, child) {
-  return (_.intersection(parent, child)).length == child.length
-}
-
-// Checks if the statement pertains to the given resource. Returns a boolean.
-function pertainsTo(statement, resource) {
-  let resources = statement.Resource
-
-  for (let i = 0; i < resources.length; i++) {
-    if (_.startsWith(resources[i], resource)) return true
-  }
-
-  return false
-}
-
-// Create an s3 Allow Statement.
-function createStatement(action, resource) {
-  return {
-    Sid: '',
-    Effect: 'Allow',
-    Principal: { 'AWS': ['*'] },
-    Action: action,
-    Resource: [ resource ]
-  }
-}
-
-// AWS will sometimes drop arrays of length 1 for their values, so normalize
-// these back to arrays with length 1.
-function normalizeStatement(statement) {
-  if (typeof statement.Principal.AWS === 'string')
-    statement.Principal.AWS = [ statement.Principal.AWS ]
-
-  if (typeof statement.Action === 'string')
-    statement.Action = [ statement.Action ]
-
-  if (typeof statement.Resource === 'string')
-    statement.Resource = [ statement.Resource ]
-
-  return statement
-}
diff --git a/src/main/minio.js b/src/main/minio.js
index 24615ca..870347f 100644
--- a/src/main/minio.js
+++ b/src/main/minio.js
@@ -39,11 +39,6 @@ import { isValidPrefix, isValidEndpoint, isValidBucketName,
 
 import { signV4, presignSignatureV4, postPresignSignatureV4 } from './signing.js'
 
-import { isValidBucketPolicy, generateBucketPolicy,
-  parseBucketPolicy } from './bucket-policy'
-
-export { Policy } from './bucket-policy'
-
 import ObjectUploader from './object-uploader'
 
 import * as transformers from './transformers'
@@ -1421,16 +1416,12 @@ export class Client {
   //
   // __Arguments__
   // * `bucketName` _string_: name of the bucket
-  // * `objectPrefix` _string_: object prefix
   // * `callback(err, policy)` _function_: callback function
-  getBucketPolicy(bucketName, objectPrefix, cb) {
+  getBucketPolicy(bucketName, cb) {
     // Validate arguments.
     if (!isValidBucketName(bucketName)) {
       throw new errors.InvalidBucketNameError(`Invalid bucket name: ${bucketName}`)
     }
-    if (!isValidPrefix(objectPrefix)) {
-      throw new errors.InvalidPrefixError(`Invalid object prefix: ${objectPrefix}`)
-    }
     if (!isFunction(cb)) {
       throw new TypeError('callback should be of type "function"')
     }
@@ -1440,14 +1431,12 @@ export class Client {
     this.makeRequest({method, bucketName, query}, '', 200, '', true, (e, response) => {
       if (e) return cb(e)
 
-      let policy = ''
-
-      let transformer = transformers.getBucketPolicyTransformer(bucketName, objectPrefix)
-      pipesetup(response, transformer)
+      let policy = new Buffer('')
+      pipesetup(response, transformers.getConcater())
         .on('data', data => policy = data)
         .on('error', cb)
         .on('end', () => {
-          cb(null, parseBucketPolicy(policy, bucketName, objectPrefix))
+          cb(null, policy.toString())
         })
     })
   }
@@ -1456,20 +1445,15 @@ export class Client {
   //
   // __Arguments__
   // * `bucketName` _string_: name of the bucket
-  // * `objectPrefix` _string_: object prefix
-  // * `bucketPolicy` _string_: bucket policy ('none', 'readonly', 'writeonly', or 'readwrite')
+  // * `bucketPolicy` _string_: bucket policy (JSON stringify'ed)
   // * `callback(err)` _function_: callback function
-  setBucketPolicy(bucketName, objectPrefix, bucketPolicy, cb) {
+  setBucketPolicy(bucketName, policy, cb) {
     // Validate arguments.
     if (!isValidBucketName(bucketName)) {
       throw new errors.InvalidBucketNameError(`Invalid bucket name: ${bucketName}`)
     }
-    if (!isValidPrefix(objectPrefix)) {
-      throw new errors.InvalidPrefixError(`Invalid object prefix: ${objectPrefix}`)
-    }
-    if (!isValidBucketPolicy(bucketPolicy)) {
-      throw new errors.InvalidBucketPolicyError(`Invalid bucket policy: ${bucketPolicy}` +
-                                                `(must be 'none', 'readonly', 'writeonly', or 'readwrite')`)
+    if (!isString(policy)) {
+      throw new errors.InvalidBucketPolicyError(`Invalid bucket policy: ${policy} - must be "string"`)
     }
     if (!isFunction(cb)) {
       throw new TypeError('callback should be of type "function"')
@@ -1477,19 +1461,12 @@ export class Client {
 
     let method = 'DELETE'
     let query = 'policy'
-    let policyPayload = ''
 
-    // For 'none', we DELETE the bucket policy instead of doing a PUT request.
-    // If it is anything but, generate the bucket policy and send it as payload.
-    if (bucketPolicy != 'none') {
+    if (policy) {
       method = 'PUT'
-
-      // Generate the statements for S3.
-      policyPayload = generateBucketPolicy(bucketPolicy, bucketName, objectPrefix)
-      policyPayload = JSON.stringify(policyPayload)
     }
 
-    this.makeRequest({method, bucketName, query}, policyPayload, 204, '', false, cb)
+    this.makeRequest({method, bucketName, query}, policy, 204, '', false, cb)
   }
 
   // Generate a generic presigned URL which can be
diff --git a/src/main/transformers.js b/src/main/transformers.js
index 8924e12..b6d2f46 100644
--- a/src/main/transformers.js
+++ b/src/main/transformers.js
@@ -208,8 +208,3 @@ export function getNotificationTransformer() {
   // This will parse and return each object.
   return new JSONParser()
 }
-
-// Parses GET BucketPolicy response.
-export function getBucketPolicyTransformer() {
-  return getConcater(response => JSON.parse(response))
-}


Test patch:
diff --git a/src/test/functional/functional-tests.js b/src/test/functional/functional-tests.js
index a021b0a..2a5dad5 100644
--- a/src/test/functional/functional-tests.js
+++ b/src/test/functional/functional-tests.js
@@ -36,8 +36,6 @@ try {
   minio = require('minio')
 }
 
-const Policy = minio.Policy
-
 require('source-map-support').install()
 
 describe('functional tests', function() {
@@ -628,98 +626,24 @@ describe('functional tests', function() {
       client.removeObject(bucketName, _5mbObjectName, done)
     })
   })
-  let policyNotImplemented = false
-  describe('bucket policy', () => {
-    let policies = [Policy.READONLY, Policy.WRITEONLY, Policy.READWRITE]
-
-    // Iterate through the basic policies ensuring it can set and check each of them.
-    policies.forEach(policy => {
-      policyNotImplemented = false
-      step(`setBucketPolicy(bucketName, objectPrefix, bucketPolicy, cb)_bucketName:${bucketName}, bucketPolicy:${policy}_`, done => {
-        client.setBucketPolicy(bucketName, '', policy, err => {
-          if (err) {
-            if(err.code != 'NotImplemented') return done(err)
-            else policyNotImplemented = true
-          }
-          done()
-        })
-      })
-      step(`getBucketPolicy(bucketName, objectPrefix, cb)_bucketName:${bucketName}_`, done => {
-        if (policyNotImplemented) return done()
-        client.getBucketPolicy(bucketName, '', (err, response) => {
-          if (err) return done(err)
-          if (response != policy) {
-            return done(new Error(`policy is incorrect (${response} != ${policy})`))
-          }
-          done()
-        })
-      })
-    })
 
-    step(`setBucketPolicy(bucketName, objectPrefix, bucketPolicy)_bucketName:${bucketName}, bucketPolicy:READONLY_`, done => {
-      client.setBucketPolicy(bucketName, '', Policy.READONLY)
-        .then(() => done())
-        .catch(done)
-    })
-
-    step(`getBucketPolicy(bucketName, objectPrefix)_bucketName:${bucketName}_`, done => {
-      client.getBucketPolicy(bucketName, '')
-        .then(response => {
-          if (response != Policy.READONLY)
-            return done(new Error(`policy is incorrect (${response} != ${Policy.READONLY})`))
-          done()
-        })
-        .catch(done)
-    })
-
-    step(`setBucketPolicy(bucketName, objectPrefix, bucketPolicy)_bucketName: ${bucketName}, objectPrefix: prefix, bucketPolicy: READWRITE_`, done => {
-      policyNotImplemented = false
-      client.setBucketPolicy(bucketName, 'prefix', Policy.READWRITE, err => {
-        if (err) {
-          if(err.code != 'NotImplemented') return done(err)
-          else policyNotImplemented = true
-        }
-        done()
-      })
-    })
-
-    step(`getBucketPolicy(bucketName, objectPrefix)_bucketName: ${bucketName}, objectPrefix: prefix_`, done => {
-      if (!policyNotImplemented) {
-        client.getBucketPolicy(bucketName, 'prefix')
-          .then(response => {
-            if (response != Policy.READWRITE)
-              return done(new Error(`policy is incorrect (${response} != ${Policy.READWRITE})`))
-            done()
-          })
-          .catch(done)
-      } else done()
-    })
-
-    step(`getBucketPolicy(bucketName, objectPrefix)_bucketName:${bucketName}, objectPrefix: wrongprefix_`, done => {
-      client.getBucketPolicy(bucketName, 'wrongprefix')
-        .then(response => {
-          if (response == Policy.READWRITE)
-            return done(new Error(`policy is incorrect (${response} == ${Policy.READWRITE})`))
-          done()
-        })
-        .catch(done)
-    })
+  describe('bucket policy', () => {
+    let policy = `{"Version":"2012-10-17","Statement":[{"Action":["s3:GetBucketLocation"],"Effect":"Allow","Principal":{"AWS":["*"]},"Resource":["arn:aws:s3:::${bucketName}"],"Sid":""},{"Action":["s3:ListBucket"],"Condition":{"StringEquals":{"s3:prefix":["foo","prefix/"]}},"Effect":"Allow","Principal":{"AWS":["*"]},"Resource":["arn:aws:s3:::${bucketName}"],"Sid":""},{"Action":["s3:GetObject"],"Effect":"Allow","Principal":{"AWS":["*"]},"Resource":["arn:aws:s3:::${bucketName}/foo*","arn:aws:s3:::${bucketName}/prefix/*"],"Sid":""}]}`
 
-    step(`setBucketPolicy(bucketName, objectPrefix, bucketPolicy, cb)_bucketName:${bucketName}, bucketPolicy: NONE_`, done => {
-      client.setBucketPolicy(bucketName, '', Policy.NONE, err => {
+    step(`setBucketPolicy(bucketName, bucketPolicy, cb)_bucketName:${bucketName}, bucketPolicy:${policy}_`, done => {
+      client.setBucketPolicy(bucketName, policy, err => {
+        if (err && err.code == 'NotImplemented') return done()
         if (err) return done(err)
         done()
       })
     })
 
-    step(`getBucketPolicy(bucketName, objectPrefix, cb)_bucketName:${bucketName}_`, done => {
-      // Check using the client — this should error.
-      client.getBucketPolicy(bucketName, '', (err) => {
-        if (!err) return done(new Error('getBucketPolicy should error'))
-
-        if (!(/does not have a bucket policy/.test(err.message)) &&
-                      !(/bucket policy does not exist/.test(err.message))) {
-          return done(new Error(`error message is incorrect (${err.message})`))
+    step(`getBucketPolicy(bucketName, cb)_bucketName:${bucketName}_`, done => {
+      client.getBucketPolicy(bucketName, (err, response) => {
+        if (err && err.code == 'NotImplemented') return done()
+        if (err) return done(err)
+        if (response != policy) {
+          return done(new Error(`policy is incorrect (${response} != ${policy})`))
         }
         done()
       })
diff --git a/src/test/unit/test.js b/src/test/unit/test.js
index de83c97..5daf472 100644
--- a/src/test/unit/test.js
+++ b/src/test/unit/test.js
@@ -22,8 +22,6 @@ import Stream from 'stream'
 import * as Minio from '../../../dist/main/minio'
 import { isValidEndpoint, isValidIP, makeDateLong, makeDateShort } from '../../../dist/main/helpers'
 
-import { parseBucketPolicy, generateBucketPolicy } from '../../../dist/main/bucket-policy'
-
 var Package = require('../../../package.json')
 
 describe('Helpers', () => {
@@ -326,72 +324,6 @@ describe('Client', function() {
     })
   })
 
-  describe('bucket policy', () => {
-    describe('constants', () => {
-      it('should equal string values', () => {
-        assert.equal(Minio.Policy.NONE, 'none')
-        assert.equal(Minio.Policy.READONLY, 'readonly')
-        assert.equal(Minio.Policy.READWRITE, 'readwrite')
-        assert.equal(Minio.Policy.WRITEONLY, 'writeonly')
-      })
-    })
-    describe('internal #parseBucketPolicy(policy, bucketName, objectPrefix)', () => {
-      it('should parse Policy.READONLY', () => {
-        let payload = JSON.parse('{"Version":"2012-10-17","Statement":[{"Sid":"","Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:GetBucketLocation","s3:ListBucket"],"Resource":["arn:aws:s3:::bucket"]},{"Sid":"","Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:GetObject"],"Resource":["arn:aws:s3:::bucket/*"]}]}')
-
-        assert.equal(parseBucketPolicy(payload, 'bucket', ''), Minio.Policy.READONLY)
-      })
-      it('should parse Policy.WRITEONLY', () => {
-        let payload = JSON.parse('{"Version":"2012-10-17","Statement":[{"Sid":"","Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:GetBucketLocation","s3:ListBucketMultipartUploads"],"Resource":["arn:aws:s3:::bucket"]},{"Sid":"","Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:AbortMultipartUpload","s3:DeleteObject","s3:ListMultipartUploadParts","s3:PutObject"],"Resource":["arn:aws:s3:::bucket/*"]}]}')
-
-        assert.equal(parseBucketPolicy(payload, 'bucket', ''), Minio.Policy.WRITEONLY)
-      })
-      it('should parse Policy.READWRITE', () => {
-        let payload = JSON.parse('{"Version":"2012-10-17","Statement":[{"Sid":"","Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:GetBucketLocation","s3:ListBucketMultipartUploads"],"Resource":["arn:aws:s3:::bucket"]},{"Sid":"","Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:AbortMultipartUpload","s3:DeleteObject","s3:ListMultipartUploadParts","s3:PutObject","s3:GetObject"],"Resource":["arn:aws:s3:::bucket/*"]}]}')
-
-        assert.equal(parseBucketPolicy(payload, 'bucket', ''), Minio.Policy.READWRITE)
-      })
-      it('should parse AWS Policy.READONLY', () => {
-        let payload = JSON.parse('{"Version":"2012-10-17","Statement":[{"Sid":"","Effect":"Allow","Principal":{"AWS":"*"},"Action":"s3:GetBucketLocation","Resource":"arn:aws:s3:::bucket"},{"Sid":"","Effect":"Allow","Principal":{"AWS":"*"},"Action":"s3:GetObject","Resource":"arn:aws:s3:::bucket/*"},{"Sid":"","Effect":"Allow","Principal":{"AWS":"*"},"Action":"s3:ListBucket","Resource":"arn:aws:s3:::bucket"}]}')
-
-        assert.equal(parseBucketPolicy(payload, 'bucket', ''), Minio.Policy.READONLY)
-      })
-      it('should parse AWS Policy.READWRITE', () => {
-        // AWS drops the array for the string, so the parser should normalize this back.
-        let payload = JSON.parse('{"Version":"2012-10-17","Statement":[{"Sid":"","Effect":"Allow","Principal":{"AWS":"*"},"Action":["s3:GetBucketLocation","s3:ListBucketMultipartUploads"],"Resource":"arn:aws:s3:::bucket"},{"Sid":"","Effect":"Allow","Principal":{"AWS":"*"},"Action":["s3:GetObject","s3:AbortMultipartUpload","s3:DeleteObject","s3:ListMultipartUploadParts","s3:PutObject"],"Resource":"arn:aws:s3:::bucket/*"},{"Sid":"","Effect":"Allow","Principal":{"AWS":"*"},"Action":"s3:ListBucket","Resource":"arn:aws:s3:::bucket"}]}')
-
-        assert.equal(parseBucketPolicy(payload, 'bucket', ''), Minio.Policy.READWRITE)
-      })
-      it('should parse Policy.NONE', () => {
-        let payload = JSON.parse('{"Version":"2012-10-17","Statement":[{"Sid":"","Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:DeleteObject"],"Resource":["arn:aws:s3:::bucket/*"]}]}')
-
-        assert.equal(parseBucketPolicy(payload, 'bucket', ''), Minio.Policy.NONE)
-      })
-    })
-    describe('internal #generateBucketPolicy(policy, bucketName, objectPrefix)', () => {
-      it('should handle Policy.READONLY', () => {
-        let payload = generateBucketPolicy(Minio.Policy.READONLY, 'bucket5', '')
-
-        assert.equal(JSON.stringify(payload), '{"Version":"2012-10-17","Statement":[{"Sid":"","Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:GetBucketLocation"],"Resource":["arn:aws:s3:::bucket5"]},{"Sid":"","Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:GetObject"],"Resource":["arn:aws:s3:::bucket5/*"]},{"Sid":"","Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:ListBucket"],"Resource":["arn:aws:s3:::bucket5"]}]}')
-      })
-      it('should handle Policy.WRITEONLY', () => {
-        let payload = generateBucketPolicy(Minio.Policy.WRITEONLY, 'bucket6', '')
-
-        assert.equal(JSON.stringify(payload), '{"Version":"2012-10-17","Statement":[{"Sid":"","Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:GetBucketLocation","s3:ListBucketMultipartUploads"],"Resource":["arn:aws:s3:::bucket6"]},{"Sid":"","Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:AbortMultipartUpload","s3:DeleteObject","s3:ListMultipartUploadParts","s3:PutObject"],"Resource":["arn:aws:s3:::bucket6/*"]}]}')
-      })
-      it('should handle Policy.READWRITE', () => {
-        let payload = generateBucketPolicy(Minio.Policy.READWRITE, 'bucket8', '')
-
-        assert.equal(JSON.stringify(payload), '{"Version":"2012-10-17","Statement":[{"Sid":"","Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:GetBucketLocation","s3:ListBucketMultipartUploads"],"Resource":["arn:aws:s3:::bucket8"]},{"Sid":"","Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:GetObject","s3:AbortMultipartUpload","s3:DeleteObject","s3:ListMultipartUploadParts","s3:PutObject"],"Resource":["arn:aws:s3:::bucket8/*"]},{"Sid":"","Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:ListBucket"],"Resource":["arn:aws:s3:::bucket8"]}]}')
-      })
-      it('should handle Policy.READWRITE with prefix', () => {
-        let payload = generateBucketPolicy(Minio.Policy.READWRITE, 'bucket9', 'prefix')
-
-        assert.equal(JSON.stringify(payload), '{"Version":"2012-10-17","Statement":[{"Sid":"","Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:GetBucketLocation","s3:ListBucketMultipartUploads"],"Resource":["arn:aws:s3:::bucket9"]},{"Sid":"","Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:GetObject","s3:AbortMultipartUpload","s3:DeleteObject","s3:ListMultipartUploadParts","s3:PutObject"],"Resource":["arn:aws:s3:::bucket9/prefix*"]},{"Sid":"","Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:ListBucket"],"Resource":["arn:aws:s3:::bucket9"],"Condition":{"StringEquals":{"s3:prefix":"prefix"}}}]}')
-      })
-    })
-  })
-
   describe('object level', () => {
     describe('#getObject(bucket, object, callback)', () => {
       it('should fail on null bucket', (done) => {