diff --git a/src/endpoint/s3/ops/s3_put_bucket_lifecycle.js b/src/endpoint/s3/ops/s3_put_bucket_lifecycle.js index 6e77135df0..84d810e148 100644 --- a/src/endpoint/s3/ops/s3_put_bucket_lifecycle.js +++ b/src/endpoint/s3/ops/s3_put_bucket_lifecycle.js @@ -3,6 +3,7 @@ const _ = require('lodash'); const s3_const = require('../s3_constants'); +const s3_utils = require('../s3_utils'); const { v4: uuid } = require('uuid'); const dbg = require('../../../util/debug_module')(__filename); const S3Error = require('../s3_errors').S3Error; @@ -108,9 +109,10 @@ async function put_bucket_lifecycle(req) { } id_set.add(current_rule.id); - if (rule.Status?.length !== 1) { - dbg.error('Rule should have status', rule); - throw new S3Error(S3Error.InvalidArgument); + if (!rule.Status || rule.Status.length !== 1 || + (rule.Status[0] !== s3_const.LIFECYCLE_STATUS.STAT_ENABLED && rule.Status[0] !== s3_const.LIFECYCLE_STATUS.STAT_DISABLED)) { + dbg.error(`Rule should have a status value of "${s3_const.LIFECYCLE_STATUS.STAT_ENABLED}" or "${s3_const.LIFECYCLE_STATUS.STAT_DISABLED}".`, rule); + throw new S3Error(S3Error.MalformedXML); } current_rule.status = rule.Status[0]; @@ -171,10 +173,14 @@ async function put_bucket_lifecycle(req) { return current_rule; }); - await req.object_sdk.set_bucket_lifecycle_configuration_rules({ - name: req.params.bucket, - rules: lifecycle_rules - }); + try { + await req.object_sdk.set_bucket_lifecycle_configuration_rules({ + name: req.params.bucket, + rules: lifecycle_rules + }); + } catch (error) { + s3_utils.invalid_schema_to_aws_error(error); + } dbg.log0('set_bucket_lifecycle', lifecycle_rules); } diff --git a/src/endpoint/s3/ops/s3_put_bucket_policy.js b/src/endpoint/s3/ops/s3_put_bucket_policy.js index a17c841475..8563fbea61 100644 --- a/src/endpoint/s3/ops/s3_put_bucket_policy.js +++ b/src/endpoint/s3/ops/s3_put_bucket_policy.js @@ -2,6 +2,7 @@ 'use strict'; const S3Error = require('../s3_errors').S3Error; +const s3_utils = require('../s3_utils'); /** * http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketPUTpolicy.html @@ -17,12 +18,7 @@ async function put_bucket_policy(req) { try { await req.object_sdk.put_bucket_policy({ name: req.params.bucket, policy }); } catch (error) { - let err = error; - if (error.rpc_code === "INVALID_SCHEMA" || error.rpc_code === "INVALID_SCHEMA_PARAMS") { - err = new S3Error(S3Error.MalformedPolicy); - err.message = "Policy was not well formed or did not validate against the published schema"; - } - throw err; + s3_utils.invalid_schema_to_aws_error(error); } } diff --git a/src/endpoint/s3/s3_constants.js b/src/endpoint/s3/s3_constants.js index 7399f6039f..60b3b5c9bf 100644 --- a/src/endpoint/s3/s3_constants.js +++ b/src/endpoint/s3/s3_constants.js @@ -10,3 +10,7 @@ const s3_const = exports; /////////////// s3_const.MAX_RULE_ID_LENGTH = 255; +s3_const.LIFECYCLE_STATUS = Object.freeze({ + STAT_ENABLED: 'Enabled', + STAT_DISABLED: 'Disabled' +}); diff --git a/src/endpoint/s3/s3_errors.js b/src/endpoint/s3/s3_errors.js index fe467fe4ab..fdc0c5de6e 100644 --- a/src/endpoint/s3/s3_errors.js +++ b/src/endpoint/s3/s3_errors.js @@ -271,7 +271,8 @@ S3Error.MalformedPOSTRequest = Object.freeze({ }); S3Error.MalformedXML = Object.freeze({ code: 'MalformedXML', - message: 'This happens when the user sends malformed xml (xml that doesn\'t conform to the published xsd) for the configuration. The error message is, "The XML you provided was not well-formed or did not validate against our published schema."', + // This happens when the user sends malformed xml (xml that doesn't conform to the published xsd) for the configuration. + message: 'The XML you provided was not well-formed or did not validate against our published schema.', http_code: 400, }); S3Error.InvalidTag = Object.freeze({ @@ -517,7 +518,8 @@ S3Error.XAmzContentSHA256Mismatch = Object.freeze({ }); S3Error.MalformedPolicy = Object.freeze({ code: 'MalformedPolicy', - message: 'Invalid principal in policy', + // This happens when policy is not well-formed or does not validate against the published schema + message: 'Your policy contains a principal that is not valid.', http_code: 400, detail: '...', // will be overridden from rpc_data, see handle_error in s3_rest.js }); diff --git a/src/endpoint/s3/s3_utils.js b/src/endpoint/s3/s3_utils.js index c2315b0b46..87fbd5a899 100644 --- a/src/endpoint/s3/s3_utils.js +++ b/src/endpoint/s3/s3_utils.js @@ -785,6 +785,24 @@ function key_marker_to_cont_tok(key_marker, objects_arr, is_truncated) { return Buffer.from(j).toString('base64'); } +/** + * invalid_schema_to_aws_error handles specific AWS error responses related to invalid schema or parameters + * If the error's `rpc_code` is either `INVALID_SCHEMA` or `INVALID_SCHEMA_PARAMS`, + * the function throws a `MalformedPolicy` error. Otherwise, it throws the original error. + * + * @param {Object} error - The error object to handle + * @throws {Error|S3Error} - Throws an appropriate error based on the input + */ +function invalid_schema_to_aws_error(error) { + if (!error) { + throw new Error("Invalid error object: Error is undefined or null."); + } + if (["INVALID_SCHEMA", "INVALID_SCHEMA_PARAMS"].includes(error.rpc_code)) { + throw new S3Error(S3Error.MalformedPolicy); + } + throw error; +} + exports.STORAGE_CLASS_STANDARD = STORAGE_CLASS_STANDARD; exports.STORAGE_CLASS_GLACIER = STORAGE_CLASS_GLACIER; exports.STORAGE_CLASS_GLACIER_IR = STORAGE_CLASS_GLACIER_IR; @@ -827,6 +845,7 @@ exports.get_default_object_owner = get_default_object_owner; exports.set_response_supported_storage_classes = set_response_supported_storage_classes; exports.cont_tok_to_key_marker = cont_tok_to_key_marker; exports.key_marker_to_cont_tok = key_marker_to_cont_tok; +exports.invalid_schema_to_aws_error = invalid_schema_to_aws_error; exports.parse_sse_c = parse_sse_c; exports.OBJECT_ATTRIBUTES = OBJECT_ATTRIBUTES; exports.OBJECT_ATTRIBUTES_UNSUPPORTED = OBJECT_ATTRIBUTES_UNSUPPORTED; diff --git a/src/test/lifecycle/common.js b/src/test/lifecycle/common.js index 61c06896aa..18c0763af9 100644 --- a/src/test/lifecycle/common.js +++ b/src/test/lifecycle/common.js @@ -569,3 +569,17 @@ exports.test_rule_duplicate_id = async function(Bucket, Key, s3) { assert(error.code === 'InvalidArgument', 'Expected InvalidArgument: duplicate ID found in the rules'); } }; + +exports.test_rule_status_value = async function(Bucket, Key, s3) { + const putLifecycleParams = id_lifecycle_configuration(Bucket, Key); + + // set the status value to an invalid value - other than 'Enabled' and 'Disabled' + putLifecycleParams.LifecycleConfiguration.Rules[0].Status = 'enabled'; + + try { + await s3.putBucketLifecycleConfiguration(putLifecycleParams); + assert.fail('Expected MalformedXML error due to wrong status value, but received a different response'); + } catch (error) { + assert(error.code === 'MalformedXML', `Expected MalformedXML error: due to invalid status value`); + } +}; diff --git a/src/test/system_tests/test_lifecycle.js b/src/test/system_tests/test_lifecycle.js index 7b23c0d1d6..b2693e8363 100644 --- a/src/test/system_tests/test_lifecycle.js +++ b/src/test/system_tests/test_lifecycle.js @@ -56,6 +56,7 @@ async function main() { await commonTests.test_and_prefix_size(Bucket, Key, s3); await commonTests.test_rule_id_length(Bucket, Key, s3); await commonTests.test_rule_duplicate_id(Bucket, Key, s3); + await commonTests.test_rule_status_value(Bucket, Key, s3); const getObjectParams = { Bucket,