From 80434a1777cd2ac6a83809bd3ce4a3a8fdacc01b Mon Sep 17 00:00:00 2001 From: DarkIsDude Date: Wed, 17 Dec 2025 09:11:45 +0100 Subject: [PATCH 01/12] =?UTF-8?q?=F0=9F=93=9D=20fix=20the=20RELEASE=20docu?= =?UTF-8?q?mentation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/RELEASE.md | 53 +++++++++++++++++++------------------------------ 1 file changed, 20 insertions(+), 33 deletions(-) diff --git a/docs/RELEASE.md b/docs/RELEASE.md index 53c1c80732..bc2aeeafee 100644 --- a/docs/RELEASE.md +++ b/docs/RELEASE.md @@ -26,42 +26,29 @@ docker pull ghcr.io/scality/cloudserver: To release a production image: -* Create a PR to bump the package version - Update Cloudserver's `package.json` by bumping it to the relevant next - version in a new PR. Per example if the last released version was - `8.4.7`, the next version would be `8.4.8`. - -```js -{ - "name": "cloudserver", - "version": "8.4.8", <--- Here - [...] -} -``` +* Create a PR to bump the package version : update Cloudserver's `package.json` by bumping it to the relevant next version in a new PR. Per example if the last released version was `8.4.7`, the next version would be `8.4.8`. + + ```js + { + "name": "cloudserver", + "version": "8.4.8", <--- Here + [...] + } + ``` * Review & merge the PR -* Create the release on GitHub - - * Go the Release tab (https://github.com/scality/cloudserver/releases); - * Click on the `Draft new release button`; - * In the `tag` field, type the name of the release (`8.4.8`), and confirm - to create the tag on publish; - * Click on `Generate release notes` button to fill the fields; - * Rename the release to `Release x.y.z` (e.g. `Release 8.4.8` in this case); - * Click to `Publish the release` to create the GitHub release and git tag - - Notes: - * the Git tag will be created automatically. - * this should be done as soon as the PR is merged, so that the tag - is put on the "version bump" commit. - -* With the following parameters, [force a build here](https://eve.devsca.com/github/scality/cloudserver/#/builders/3/force/force) - - * Branch Name: The one used for the tag earlier. In this example `development/8.4` - * Override Stage: 'release' - * Extra properties: - * name: `'tag'`, value: `[release version]`, in this example`'8.4.8'` +* Trigger the release workflow on GitHub + + * Go to the [**Actions** tab on GitHub](https://github.com/scality/cloudserver/actions) + * Select the `release` workflow from the list + * Click on **Run workflow** (manual dispatch) + * Enter the new tag (e.g., `8.4.8`) in the input field + * Start the workflow + + This workflow will create the tag and push the Docker images. + + This should be done as soon as the PR is merged, so that the tag is put on the "version bump" commit. * Release the release version on Jira From 8e924b321d914e4d01e89bee1e9ba1021fe940ed Mon Sep 17 00:00:00 2001 From: DarkIsDude Date: Wed, 17 Dec 2025 09:12:08 +0100 Subject: [PATCH 02/12] =?UTF-8?q?=F0=9F=94=A7=20add=20pino-prettier=20to?= =?UTF-8?q?=20the=20dev=20script?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 3 +- yarn.lock | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 101 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index a228a88474..a323c66efd 100644 --- a/package.json +++ b/package.json @@ -69,6 +69,7 @@ "node-mocks-http": "^1.16.1", "nodemon": "^3.1.10", "nyc": "^15.1.0", + "pino-pretty": "^13.1.3", "sinon": "^13.0.1", "tv4": "^1.3.0" }, @@ -82,7 +83,7 @@ }, "scripts": { "cloudserver": "S3METADATA=mongodb npm-run-all --parallel start_dataserver start_s3server", - "dev": "nodemon --exec \"yarn run start\"", + "dev": "nodemon --exec \"yarn run start\" | pino-pretty -c -S -m message -i \"pid,hostname,name,authn,authz,address,clientPort,clientIP,elapsed_ms\"", "ft_awssdk": "cd tests/functional/aws-node-sdk && mocha --reporter mocha-multi-reporters --reporter-options configFile=$INIT_CWD/tests/reporter-config.json test/ --exit", "ft_awssdk_aws": "cd tests/functional/aws-node-sdk && AWS_ON_AIR=true mocha --reporter mocha-multi-reporters --reporter-options configFile=$INIT_CWD/tests/reporter-config.json test/ --exit", "ft_awssdk_buckets": "cd tests/functional/aws-node-sdk && mocha --reporter mocha-multi-reporters --reporter-options configFile=$INIT_CWD/tests/reporter-config.json test/bucket --exit", diff --git a/yarn.lock b/yarn.lock index c018a3859c..6a7621b45b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1573,6 +1573,11 @@ asynckit@^0.4.0: resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== +atomic-sleep@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/atomic-sleep/-/atomic-sleep-1.0.0.tgz#eb85b77a601fc932cfe432c5acd364a9e2c9075b" + integrity sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ== + available-typed-arrays@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846" @@ -2022,6 +2027,11 @@ color@^3.1.3: color-convert "^1.9.3" color-string "^1.6.0" +colorette@^2.0.7: + version "2.0.20" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" + integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== + colorspace@1.1.x: version "1.1.4" resolved "https://registry.yarnpkg.com/colorspace/-/colorspace-1.1.4.tgz#8d442d1186152f60453bf8070cd66eb364e59243" @@ -2201,6 +2211,11 @@ data-view-byte-offset@^1.0.1: es-errors "^1.3.0" is-data-view "^1.0.1" +dateformat@^4.6.3: + version "4.6.3" + resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-4.6.3.tgz#556fa6497e5217fedb78821424f8a1c22fa3f4b5" + integrity sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA== + dayjs@^1.11.10: version "1.11.15" resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.15.tgz#fd7fd2db6fc92f08ffe4adc306756d45db00ada3" @@ -2461,6 +2476,13 @@ encoding@^0.1.13: dependencies: iconv-lite "^0.6.2" +end-of-stream@^1.1.0: + version "1.4.5" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.5.tgz#7344d711dea40e0b74abc2ed49778743ccedb08c" + integrity sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg== + dependencies: + once "^1.4.0" + engine.io-client@~6.6.1: version "6.6.3" resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-6.6.3.tgz#815393fa24f30b8e6afa8f77ccca2f28146be6de" @@ -2968,6 +2990,11 @@ extsprintf@^1.2.0: resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.1.tgz#8d172c064867f235c0c84a596806d279bf4bcc07" integrity sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA== +fast-copy@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/fast-copy/-/fast-copy-4.0.1.tgz#be5c74baede1a72adf8168df2dc56e842c77a00e" + integrity sha512-+uUOQlhsaswsizHFmEFAQhB3lSiQ+lisxl50N6ZP0wywlZeWsIESxSi9ftPEps8UGfiBzyYP7x27zA674WUvXw== + fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" @@ -3534,6 +3561,11 @@ he@^1.2.0: resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== +help-me@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/help-me/-/help-me-5.0.0.tgz#b1ebe63b967b74060027c2ac61f9be12d354a6f6" + integrity sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg== + hosted-git-info@^2.1.4: version "2.8.9" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" @@ -4298,6 +4330,11 @@ joi@^18.0.1: "@hapi/topo" "^6.0.2" "@standard-schema/spec" "^1.0.0" +joycon@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/joycon/-/joycon-3.1.1.tgz#bce8596d6ae808f8b68168f5fc69280996894f03" + integrity sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw== + js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -5454,6 +5491,11 @@ object.values@^1.2.0: define-properties "^1.2.1" es-object-atoms "^1.0.0" +on-exit-leak-free@^2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz#fed195c9ebddb7d9e4c3842f93f281ac8dadd3b8" + integrity sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA== + on-finished@2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" @@ -5461,7 +5503,7 @@ on-finished@2.4.1: dependencies: ee-first "1.1.1" -once@1.x, once@^1.3.0, once@^1.4.0: +once@1.x, once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== @@ -5683,6 +5725,32 @@ pify@^4.0.0, pify@^4.0.1: resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== +pino-abstract-transport@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pino-abstract-transport/-/pino-abstract-transport-3.0.0.tgz#b21e5f33a297e8c4c915c62b3ce5dd4a87a52c23" + integrity sha512-wlfUczU+n7Hy/Ha5j9a/gZNy7We5+cXp8YL+X+PG8S0KXxw7n/JXA3c46Y0zQznIJ83URJiwy7Lh56WLokNuxg== + dependencies: + split2 "^4.0.0" + +pino-pretty@^13.1.3: + version "13.1.3" + resolved "https://registry.yarnpkg.com/pino-pretty/-/pino-pretty-13.1.3.tgz#2274cccda925dd355c104079a5029f6598d0381b" + integrity sha512-ttXRkkOz6WWC95KeY9+xxWL6AtImwbyMHrL1mSwqwW9u+vLp/WIElvHvCSDg0xO/Dzrggz1zv3rN5ovTRVowKg== + dependencies: + colorette "^2.0.7" + dateformat "^4.6.3" + fast-copy "^4.0.0" + fast-safe-stringify "^2.1.1" + help-me "^5.0.0" + joycon "^3.1.1" + minimist "^1.2.6" + on-exit-leak-free "^2.1.0" + pino-abstract-transport "^3.0.0" + pump "^3.0.0" + secure-json-parse "^4.0.0" + sonic-boom "^4.0.1" + strip-json-comments "^5.0.2" + pkg-dir@^4.1.0: version "4.2.0" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" @@ -5821,6 +5889,14 @@ pull-window@^2.1.4: dependencies: looper "^2.0.0" +pump@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.3.tgz#151d979f1a29668dc0025ec589a455b53282268d" + integrity sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + punycode@1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" @@ -6117,6 +6193,11 @@ sax@>=0.6.0, sax@^1.2.4: "@smithy/signature-v4" "^2.1.1" axios "^1.3.4" +secure-json-parse@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/secure-json-parse/-/secure-json-parse-4.1.0.tgz#4f1ab41c67a13497ea1b9131bb4183a22865477c" + integrity sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA== + "semver@2 || 3 || 4 || 5", semver@^5.5.0, semver@^5.6.0: version "5.7.2" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" @@ -6390,6 +6471,13 @@ socks@^2.8.3: ip-address "^9.0.5" smart-buffer "^4.2.0" +sonic-boom@^4.0.1: + version "4.2.0" + resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-4.2.0.tgz#e59a525f831210fa4ef1896428338641ac1c124d" + integrity sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww== + dependencies: + atomic-sleep "^1.0.0" + sorted-array-functions@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/sorted-array-functions/-/sorted-array-functions-1.3.0.tgz#8605695563294dffb2c9796d602bd8459f7a0dd5" @@ -6452,6 +6540,11 @@ spdx-license-ids@^3.0.0: resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.21.tgz#6d6e980c9df2b6fc905343a3b2d702a6239536c3" integrity sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg== +split2@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/split2/-/split2-4.2.0.tgz#c9c5920904d148bab0b9f67145f245a86aadbfa4" + integrity sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg== + sprintf-js@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.3.tgz#4914b903a2f8b685d17fdf78a70e917e872e444a" @@ -6640,6 +6733,11 @@ strip-json-comments@^3.1.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== +strip-json-comments@^5.0.2: + version "5.0.3" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-5.0.3.tgz#b7304249dd402ee67fd518ada993ab3593458bcf" + integrity sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw== + strnum@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/strnum/-/strnum-2.0.5.tgz#40700b1b5bf956acdc755e98e90005d7657aaaea" From 399965b18d9c5421c586a2bd51c4e80338f0d3d5 Mon Sep 17 00:00:00 2001 From: DarkIsDude Date: Wed, 17 Dec 2025 16:47:17 +0100 Subject: [PATCH 03/12] =?UTF-8?q?=E2=9C=A8=20manage=20the=20new=20header?= =?UTF-8?q?=20x-amz-optional-attributes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Issue: CLDSRV-812 --- .../authorization/prepareRequestContexts.js | 9 ++ lib/api/bucketGet.js | 17 +-- tests/unit/api/bucketGet.js | 134 +++++++++++++++--- 3 files changed, 133 insertions(+), 27 deletions(-) diff --git a/lib/api/apiUtils/authorization/prepareRequestContexts.js b/lib/api/apiUtils/authorization/prepareRequestContexts.js index c33fcc9c82..2ee280b91a 100644 --- a/lib/api/apiUtils/authorization/prepareRequestContexts.js +++ b/lib/api/apiUtils/authorization/prepareRequestContexts.js @@ -252,6 +252,15 @@ function prepareRequestContexts(apiMethod, request, sourceBucket, generateRequestContext('bypassGovernanceRetention'); requestContexts.push(checkUserGovernanceBypassRequestContext); } + } else if (apiMethodAfterVersionCheck === 'bucketGet') { + requestContexts.push(generateRequestContext(apiMethodAfterVersionCheck)); + + const optionalAttributesHeader = request.headers['x-amz-optional-attributes']; + const requestedAttributes = optionalAttributesHeader ? optionalAttributesHeader.split(',') : []; + + if (requestedAttributes.filter(attr => attr != 'RestoreStatus').length > 0) { + requestContexts.push(generateRequestContext('listObjectsV2OptionalAttributes')); + } } else { const requestContext = generateRequestContext(apiMethodAfterVersionCheck); diff --git a/lib/api/bucketGet.js b/lib/api/bucketGet.js index 8eef7694e4..6d95fa0a4f 100644 --- a/lib/api/bucketGet.js +++ b/lib/api/bucketGet.js @@ -344,14 +344,14 @@ function bucketGet(authInfo, request, log, callback) { } standardMetadataValidateBucket(metadataValParams, request.actionImplicitDenies, log, (err, bucket) => { - const corsHeaders = collectCorsHeaders(request.headers.origin, - request.method, bucket); + const corsHeaders = collectCorsHeaders(request.headers.origin, request.method, bucket); + if (err) { log.debug('error processing request', { error: err }); - monitoring.promMetrics( - 'GET', bucketName, err.code, 'listBucket'); + monitoring.promMetrics('GET', bucketName, err.code, 'listBucket'); return callback(err, null, corsHeaders); } + if (params.versions !== undefined) { listParams.listingType = 'DelimiterVersions'; delete listParams.marker; @@ -359,6 +359,7 @@ function bucketGet(authInfo, request, log, callback) { listParams.versionIdMarker = params['version-id-marker'] ? versionIdUtils.decode(params['version-id-marker']) : undefined; } + if (!requestMaxKeys) { const emptyList = { CommonPrefixes: [], @@ -369,14 +370,14 @@ function bucketGet(authInfo, request, log, callback) { return handleResult(listParams, requestMaxKeys, encoding, authInfo, bucketName, emptyList, corsHeaders, log, callback); } - return services.getObjectListing(bucketName, listParams, log, - (err, list) => { + + return services.getObjectListing(bucketName, listParams, log, (err, list) => { if (err) { log.debug('error processing request', { error: err }); - monitoring.promMetrics( - 'GET', bucketName, err.code, 'listBucket'); + monitoring.promMetrics('GET', bucketName, err.code, 'listBucket'); return callback(err, null, corsHeaders); } + return handleResult(listParams, requestMaxKeys, encoding, authInfo, bucketName, list, corsHeaders, log, callback); }); diff --git a/tests/unit/api/bucketGet.js b/tests/unit/api/bucketGet.js index ba166fd088..b793c015a9 100644 --- a/tests/unit/api/bucketGet.js +++ b/tests/unit/api/bucketGet.js @@ -184,17 +184,17 @@ describe('bucketGet API', () => { async.waterfall([ next => bucketPut(authInfo, testPutBucketRequest, log, next), - (corsHeaders, next) => objectPut(authInfo, + (_, next) => objectPut(authInfo, testPutObjectRequest1, undefined, log, next), - (resHeaders, next) => objectPut(authInfo, + (_, next) => objectPut(authInfo, testPutObjectRequest2, undefined, log, next), - (resHeaders, next) => objectPut(authInfo, + (_, next) => objectPut(authInfo, testPutObjectRequest3, undefined, log, next), - (resHeaders, next) => + (_, next) => bucketGet(authInfo, testGetRequest, log, next), - (result, corsHeaders, next) => parseString(result, next), + (result, _, next) => parseString(result, next), ], - (err, result) => { + (_, result) => { test.assertion(result); done(); }); @@ -216,13 +216,13 @@ describe('bucketGet API', () => { async.waterfall([ next => bucketPut(authInfo, testPutBucketRequest, log, next), - (corsHeaders, next) => objectPut(authInfo, testPutObjectRequest4, + (_, next) => objectPut(authInfo, testPutObjectRequest4, undefined, log, next), - (resHeaders, next) => bucketGet(authInfo, testGetRequest, + (_, next) => bucketGet(authInfo, testGetRequest, log, next), - (result, corsHeaders, next) => parseString(result, next), + (result, _, next) => parseString(result, next), ], - (err, result) => { + (_, result) => { assert.strictEqual(result.ListBucketResult.Contents[0].Key[0], testPutObjectRequest4.objectKey); done(); @@ -235,11 +235,11 @@ describe('bucketGet API', () => { async.waterfall([ next => bucketPut(authInfo, testPutBucketRequest, log, next), - (corsHeaders, next) => + (_, next) => bucketGet(authInfo, testGetRequest, log, next), - (result, corsHeaders, next) => parseString(result, next), + (result, _, next) => parseString(result, next), ], - (err, result) => { + (_, result) => { assert.strictEqual(result.ListBucketResult.$.xmlns, 'http://s3.amazonaws.com/doc/2006-03-01/'); done(); @@ -307,17 +307,17 @@ describe('bucketGet API V2', () => { async.waterfall([ next => bucketPut(authInfo, testPutBucketRequest, log, next), - (corsHeaders, next) => objectPut(authInfo, + (_, next) => objectPut(authInfo, testPutObjectRequest1, undefined, log, next), - (resHeaders, next) => objectPut(authInfo, + (_, next) => objectPut(authInfo, testPutObjectRequest2, undefined, log, next), - (resHeaders, next) => objectPut(authInfo, + (_, next) => objectPut(authInfo, testPutObjectRequest3, undefined, log, next), - (resHeaders, next) => + (_, next) => bucketGet(authInfo, testGetRequest, log, next), - (result, corsHeaders, next) => parseString(result, next), + (result, _, next) => parseString(result, next), ], - (err, result) => { + (_, result) => { // v2 requests should return 'KeyCount' in response const keyCount = Number.parseInt(result.ListBucketResult.KeyCount[0], 10); @@ -333,4 +333,100 @@ describe('bucketGet API V2', () => { }); }); }); + + describe.only('z-amz-optional-attributes header', () => { + it('should return an error if the user does not have the permission', done => { + const authInfoNoPerm = makeAuthInfo('accessKey2'); // A new user without specific permissions + const testGetRequest = Object.assign({ + query: {}, + url: baseUrl, + headers: { 'x-amz-optional-attributes': 'x-amz-meta-department' }, + }, baseGetRequest); + + async.series([ + next => bucketPut(authInfo, testPutBucketRequest, log, next), + next => bucketGet(authInfoNoPerm, testGetRequest, log, next), + ], err => { + assert.strictEqual(err.is.AccessDenied, true); + done(); + }); + }); + + it('should ignore the missing permission if the header contains only RestoreStatus', done => { + const testGetRequest = Object.assign({ + query: {}, + url: baseUrl, + headers: { 'x-amz-optional-attributes': 'RestoreStatus' }, + }, baseGetRequest); + + async.waterfall( + [ + next => bucketPut(authInfo, testPutBucketRequest, log, next), + (_, next) => bucketGet(authInfo, testGetRequest, log, next), + (result, _, next) => parseString(result, next), + ], + (err, result) => { + assert.strictEqual(err, null); + assert.strictEqual(result.ListBucketResult.$.xmlns, 'http://s3.amazonaws.com/doc/2006-03-01/'); + done(); + }, + ); + }); + + it('should return valid xml if the user have the permission', done => { + const testGetRequest = Object.assign({ + query: {}, + url: baseUrl, + headers: { 'x-amz-optional-attributes': 'x-amz-meta-department' }, + }, baseGetRequest); + + async.waterfall([ + next => bucketPut(authInfo, testPutBucketRequest, log, next), + (_, next) => bucketGet(authInfo, testGetRequest, log, next), + (result, _, next) => parseString(result, next), + ], + (err, result) => { + assert.strictEqual(err, null); + assert.strictEqual(result.ListBucketResult.$.xmlns, + 'http://s3.amazonaws.com/doc/2006-03-01/'); + done(); + }); + }); + + it('should return an error if the user does not have all permissions for multiple attributes', done => { + const authInfoNoPerm = makeAuthInfo('accessKey2'); + const testGetRequest = Object.assign({ + query: {}, + url: baseUrl, + headers: { 'x-amz-optional-attributes': 'RestoreStatus,x-amz-meta-department' }, + }, baseGetRequest); + + async.series([ + next => bucketPut(authInfo, testPutBucketRequest, log, next), + next => bucketGet(authInfoNoPerm, testGetRequest, log, next), + ], err => { + assert.strictEqual(err.is.AccessDenied, true); + done(); + }); + }); + + it('should ignore the header if the value is empty', done => { + const testGetRequest = Object.assign({ + query: {}, + url: baseUrl, + headers: { 'x-amz-optional-attributes': '' }, + }, baseGetRequest); + + async.waterfall([ + next => bucketPut(authInfo, testPutBucketRequest, log, next), + (_, next) => bucketGet(authInfo, testGetRequest, log, next), + (result, _, next) => parseString(result, next), + ], (err, result) => { + assert.strictEqual(err, null); + assert.strictEqual(result.ListBucketResult.$.xmlns, + 'http://s3.amazonaws.com/doc/2006-03-01/'); + done(); + }); + }); + }); }); From 8615aca9cf80f6b822b726172e2768a9a9cc2850 Mon Sep 17 00:00:00 2001 From: DarkIsDude Date: Thu, 18 Dec 2025 14:12:46 +0100 Subject: [PATCH 04/12] =?UTF-8?q?=E2=9C=A8=20validate=20the=20format=20of?= =?UTF-8?q?=20the=20header?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Issue: CLDSRV-812 --- .../authorization/prepareRequestContexts.js | 2 +- lib/api/bucketGet.js | 9 ++ tests/unit/api/bucketGet.js | 108 ++++++++++++++++-- 3 files changed, 107 insertions(+), 12 deletions(-) diff --git a/lib/api/apiUtils/authorization/prepareRequestContexts.js b/lib/api/apiUtils/authorization/prepareRequestContexts.js index 2ee280b91a..c62ace060a 100644 --- a/lib/api/apiUtils/authorization/prepareRequestContexts.js +++ b/lib/api/apiUtils/authorization/prepareRequestContexts.js @@ -255,7 +255,7 @@ function prepareRequestContexts(apiMethod, request, sourceBucket, } else if (apiMethodAfterVersionCheck === 'bucketGet') { requestContexts.push(generateRequestContext(apiMethodAfterVersionCheck)); - const optionalAttributesHeader = request.headers['x-amz-optional-attributes']; + const optionalAttributesHeader = request.headers['x-amz-optional-object-attributes']; const requestedAttributes = optionalAttributesHeader ? optionalAttributesHeader.split(',') : []; if (requestedAttributes.filter(attr => attr != 'RestoreStatus').length > 0) { diff --git a/lib/api/bucketGet.js b/lib/api/bucketGet.js index 6d95fa0a4f..93d03e1141 100644 --- a/lib/api/bucketGet.js +++ b/lib/api/bucketGet.js @@ -284,6 +284,15 @@ function bucketGet(authInfo, request, log, callback) { const params = request.query; const bucketName = request.bucketName; const v2 = params['list-type']; + + const optionalAttributes = + request.headers['x-amz-optional-object-attributes']?.split(',').map(attr => attr.trim()) ?? []; + if (optionalAttributes.some(attr => !attr.startsWith('x-amz-meta-') && attr != 'RestoreStatus')) { + return callback( + errorInstances.InvalidArgument.customizeDescription('Invalid header x-amz-optional-object-attributes') + ); + } + if (v2 !== undefined && Number.parseInt(v2, 10) !== 2) { return callback(errorInstances.InvalidArgument.customizeDescription('Invalid ' + 'List Type specified in Request')); diff --git a/tests/unit/api/bucketGet.js b/tests/unit/api/bucketGet.js index b793c015a9..971e58748b 100644 --- a/tests/unit/api/bucketGet.js +++ b/tests/unit/api/bucketGet.js @@ -334,14 +334,15 @@ describe('bucketGet API V2', () => { }); }); - describe.only('z-amz-optional-attributes header', () => { + describe('z-amz-optional-attributes header', () => { it('should return an error if the user does not have the permission', done => { const authInfoNoPerm = makeAuthInfo('accessKey2'); // A new user without specific permissions const testGetRequest = Object.assign({ query: {}, url: baseUrl, - headers: { 'x-amz-optional-attributes': 'x-amz-meta-department' }, }, baseGetRequest); + testGetRequest.headers['x-amz-optional-object-attributes'] = 'x-amz-meta-department'; + async.series([ next => bucketPut(authInfo, testPutBucketRequest, log, next), @@ -356,8 +357,8 @@ describe('bucketGet API V2', () => { const testGetRequest = Object.assign({ query: {}, url: baseUrl, - headers: { 'x-amz-optional-attributes': 'RestoreStatus' }, }, baseGetRequest); + testGetRequest.headers['x-amz-optional-object-attributes'] = 'RestoreStatus'; async.waterfall( [ @@ -377,8 +378,8 @@ describe('bucketGet API V2', () => { const testGetRequest = Object.assign({ query: {}, url: baseUrl, - headers: { 'x-amz-optional-attributes': 'x-amz-meta-department' }, }, baseGetRequest); + testGetRequest.headers['x-amz-optional-object-attributes'] = 'x-amz-meta-department'; async.waterfall([ next => bucketPut(authInfo, testPutBucketRequest, log, next), @@ -387,8 +388,7 @@ describe('bucketGet API V2', () => { ], (err, result) => { assert.strictEqual(err, null); - assert.strictEqual(result.ListBucketResult.$.xmlns, - 'http://s3.amazonaws.com/doc/2006-03-01/'); + assert.strictEqual(result.ListBucketResult.$.xmlns, 'http://s3.amazonaws.com/doc/2006-03-01/'); done(); }); }); @@ -398,8 +398,8 @@ describe('bucketGet API V2', () => { const testGetRequest = Object.assign({ query: {}, url: baseUrl, - headers: { 'x-amz-optional-attributes': 'RestoreStatus,x-amz-meta-department' }, }, baseGetRequest); + testGetRequest.headers['x-amz-optional-object-attributes'] = 'RestoreStatus,x-amz-meta-department'; async.series([ next => bucketPut(authInfo, testPutBucketRequest, log, next), @@ -410,12 +410,45 @@ describe('bucketGet API V2', () => { }); }); - it('should ignore the header if the value is empty', done => { + it('should return an error if the header is empty', done => { + const testGetRequest = Object.assign({ + query: {}, + url: baseUrl, + }, baseGetRequest); + testGetRequest.headers['x-amz-optional-object-attributes'] = ''; + + async.waterfall([ + next => bucketPut(authInfo, testPutBucketRequest, log, next), + (_, next) => bucketGet(authInfo, testGetRequest, log, next), + (result, _, next) => parseString(result, next), + ], err => { + assert.strictEqual(err.is.InvalidArgument, true); + done(); + }); + }); + + it('should return an error for invalid optional attributes', done => { + const testGetRequest = Object.assign({ + query: {}, + url: baseUrl, + }, baseGetRequest); + testGetRequest.headers['x-amz-optional-object-attributes'] = 'InvalidAttribute'; + + async.series([ + next => bucketPut(authInfo, testPutBucketRequest, log, next), + next => bucketGet(authInfo, testGetRequest, log, next), + ], err => { + assert.strictEqual(err.is.InvalidArgument, true); + done(); + }); + }); + + it('should accept wildcard value', done => { const testGetRequest = Object.assign({ query: {}, url: baseUrl, - headers: { 'x-amz-optional-attributes': '' }, }, baseGetRequest); + testGetRequest.headers['x-amz-optional-object-attributes'] = 'x-amz-meta-*'; async.waterfall([ next => bucketPut(authInfo, testPutBucketRequest, log, next), @@ -423,8 +456,61 @@ describe('bucketGet API V2', () => { (result, _, next) => parseString(result, next), ], (err, result) => { assert.strictEqual(err, null); - assert.strictEqual(result.ListBucketResult.$.xmlns, - 'http://s3.amazonaws.com/doc/2006-03-01/'); + assert.strictEqual(result.ListBucketResult.$.xmlns, 'http://s3.amazonaws.com/doc/2006-03-01/'); + done(); + }); + }); + + it('should return an error for a mix of valid and invalid attributes', done => { + const testGetRequest = Object.assign({ + query: {}, + url: baseUrl, + }, baseGetRequest); + testGetRequest.headers['x-amz-optional-object-attributes'] = 'RestoreStatus,InvalidAttribute'; + + async.series([ + next => bucketPut(authInfo, testPutBucketRequest, log, next), + next => bucketGet(authInfo, testGetRequest, log, next), + ], err => { + assert.strictEqual(err.is.InvalidArgument, true); + done(); + }); + }); + + it('should handle attributes with leading/trailing whitespace', done => { + const testGetRequest = Object.assign({ + query: {}, + url: baseUrl, + }, baseGetRequest); + testGetRequest.headers['x-amz-optional-object-attributes'] = 'x-amz-meta-foo'; + + async.waterfall([ + next => bucketPut(authInfo, testPutBucketRequest, log, next), + (_, next) => bucketGet(authInfo, testGetRequest, log, next), + (result, _, next) => parseString(result, next), + ], + (err, result) => { + assert.strictEqual(err, null); + assert.strictEqual(result.ListBucketResult.$.xmlns, 'http://s3.amazonaws.com/doc/2006-03-01/'); + done(); + }); + }); + + it('should handle multiple valid attributes', done => { + const testGetRequest = Object.assign({ + query: {}, + url: baseUrl, + }, baseGetRequest); + testGetRequest.headers['x-amz-optional-object-attributes'] = 'RestoreStatus,x-amz-meta-foo,x-amz-meta-bar'; + + async.waterfall([ + next => bucketPut(authInfo, testPutBucketRequest, log, next), + (_, next) => bucketGet(authInfo, testGetRequest, log, next), + (result, _, next) => parseString(result, next), + ], + (err, result) => { + assert.strictEqual(err, null); + assert.strictEqual(result.ListBucketResult.$.xmlns, 'http://s3.amazonaws.com/doc/2006-03-01/'); done(); }); }); From ac122f10d5fd5f3bea1d07817f2b33f3b9ccfc4e Mon Sep 17 00:00:00 2001 From: DarkIsDude Date: Thu, 18 Dec 2025 17:44:46 +0100 Subject: [PATCH 05/12] =?UTF-8?q?=F0=9F=9A=A8=20lint=20markdown=20RELEASE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/RELEASE.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/RELEASE.md b/docs/RELEASE.md index bc2aeeafee..ddfb379180 100644 --- a/docs/RELEASE.md +++ b/docs/RELEASE.md @@ -26,7 +26,10 @@ docker pull ghcr.io/scality/cloudserver: To release a production image: -* Create a PR to bump the package version : update Cloudserver's `package.json` by bumping it to the relevant next version in a new PR. Per example if the last released version was `8.4.7`, the next version would be `8.4.8`. +* Create a PR to bump the package version : + update Cloudserver's `package.json` by bumping it to the relevant next + version in a new PR. Per example if the last released version was `8.4.7`, + the next version would be `8.4.8`. ```js { @@ -48,7 +51,8 @@ To release a production image: This workflow will create the tag and push the Docker images. - This should be done as soon as the PR is merged, so that the tag is put on the "version bump" commit. + This should be done as soon as the PR is merged, + so that the tag is put on the "version bump" commit. * Release the release version on Jira From 18bc5fc0d70939e11b5ab81653f812a6640ef22f Mon Sep 17 00:00:00 2001 From: DarkIsDude Date: Mon, 12 Jan 2026 16:12:03 +0100 Subject: [PATCH 06/12] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20rework=20test=20to?= =?UTF-8?q?=20fix=20duplicate=20and=20typo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Issue: CLDSRV-812 --- .../authorization/prepareRequestContexts.js | 4 +- tests/unit/api/bucketGet.js | 45 +++++-------------- 2 files changed, 15 insertions(+), 34 deletions(-) diff --git a/lib/api/apiUtils/authorization/prepareRequestContexts.js b/lib/api/apiUtils/authorization/prepareRequestContexts.js index c62ace060a..3138798406 100644 --- a/lib/api/apiUtils/authorization/prepareRequestContexts.js +++ b/lib/api/apiUtils/authorization/prepareRequestContexts.js @@ -256,7 +256,9 @@ function prepareRequestContexts(apiMethod, request, sourceBucket, requestContexts.push(generateRequestContext(apiMethodAfterVersionCheck)); const optionalAttributesHeader = request.headers['x-amz-optional-object-attributes']; - const requestedAttributes = optionalAttributesHeader ? optionalAttributesHeader.split(',') : []; + const requestedAttributes = optionalAttributesHeader ? + optionalAttributesHeader.split(',').map(header => header.toLowerCase()) : + []; if (requestedAttributes.filter(attr => attr != 'RestoreStatus').length > 0) { requestContexts.push(generateRequestContext('listObjectsV2OptionalAttributes')); diff --git a/tests/unit/api/bucketGet.js b/tests/unit/api/bucketGet.js index 971e58748b..2499f3d8af 100644 --- a/tests/unit/api/bucketGet.js +++ b/tests/unit/api/bucketGet.js @@ -334,7 +334,7 @@ describe('bucketGet API V2', () => { }); }); - describe('z-amz-optional-attributes header', () => { + describe('x-amz-optional-attributes header', () => { it('should return an error if the user does not have the permission', done => { const authInfoNoPerm = makeAuthInfo('accessKey2'); // A new user without specific permissions const testGetRequest = Object.assign({ @@ -343,7 +343,6 @@ describe('bucketGet API V2', () => { }, baseGetRequest); testGetRequest.headers['x-amz-optional-object-attributes'] = 'x-amz-meta-department'; - async.series([ next => bucketPut(authInfo, testPutBucketRequest, log, next), next => bucketGet(authInfoNoPerm, testGetRequest, log, next), @@ -353,25 +352,22 @@ describe('bucketGet API V2', () => { }); }); - it('should ignore the missing permission if the header contains only RestoreStatus', done => { + it('should pass without permission if the header contains only RestoreStatus', done => { const testGetRequest = Object.assign({ query: {}, url: baseUrl, }, baseGetRequest); testGetRequest.headers['x-amz-optional-object-attributes'] = 'RestoreStatus'; - async.waterfall( - [ - next => bucketPut(authInfo, testPutBucketRequest, log, next), - (_, next) => bucketGet(authInfo, testGetRequest, log, next), - (result, _, next) => parseString(result, next), - ], - (err, result) => { - assert.strictEqual(err, null); - assert.strictEqual(result.ListBucketResult.$.xmlns, 'http://s3.amazonaws.com/doc/2006-03-01/'); - done(); - }, - ); + async.waterfall([ + next => bucketPut(authInfo, testPutBucketRequest, log, next), + (_, next) => bucketGet(authInfo, testGetRequest, log, next), + (result, _, next) => parseString(result, next), + ], (err, result) => { + assert.strictEqual(err, null); + assert.strictEqual(result.ListBucketResult.$.xmlns, 'http://s3.amazonaws.com/doc/2006-03-01/'); + done(); + }); }); it('should return valid xml if the user have the permission', done => { @@ -393,23 +389,6 @@ describe('bucketGet API V2', () => { }); }); - it('should return an error if the user does not have all permissions for multiple attributes', done => { - const authInfoNoPerm = makeAuthInfo('accessKey2'); - const testGetRequest = Object.assign({ - query: {}, - url: baseUrl, - }, baseGetRequest); - testGetRequest.headers['x-amz-optional-object-attributes'] = 'RestoreStatus,x-amz-meta-department'; - - async.series([ - next => bucketPut(authInfo, testPutBucketRequest, log, next), - next => bucketGet(authInfoNoPerm, testGetRequest, log, next), - ], err => { - assert.strictEqual(err.is.AccessDenied, true); - done(); - }); - }); - it('should return an error if the header is empty', done => { const testGetRequest = Object.assign({ query: {}, @@ -482,7 +461,7 @@ describe('bucketGet API V2', () => { query: {}, url: baseUrl, }, baseGetRequest); - testGetRequest.headers['x-amz-optional-object-attributes'] = 'x-amz-meta-foo'; + testGetRequest.headers['x-amz-optional-object-attributes'] = ' x-amz-meta-foo '; async.waterfall([ next => bucketPut(authInfo, testPutBucketRequest, log, next), From 84354a6c5b9f9ee2708d009b77ecd553f2f568b4 Mon Sep 17 00:00:00 2001 From: DarkIsDude Date: Mon, 12 Jan 2026 16:12:29 +0100 Subject: [PATCH 07/12] =?UTF-8?q?=E2=9C=A8=20parse=20user=20metadata=20as?= =?UTF-8?q?=20lowercase?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Issue: CLDSRV-812 --- lib/api/apiUtils/authorization/prepareRequestContexts.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/api/apiUtils/authorization/prepareRequestContexts.js b/lib/api/apiUtils/authorization/prepareRequestContexts.js index 3138798406..8516cd95f6 100644 --- a/lib/api/apiUtils/authorization/prepareRequestContexts.js +++ b/lib/api/apiUtils/authorization/prepareRequestContexts.js @@ -257,8 +257,9 @@ function prepareRequestContexts(apiMethod, request, sourceBucket, const optionalAttributesHeader = request.headers['x-amz-optional-object-attributes']; const requestedAttributes = optionalAttributesHeader ? - optionalAttributesHeader.split(',').map(header => header.toLowerCase()) : - []; + optionalAttributesHeader.split(',').map( + header => header !== 'RestoreStatus' ? header.toLowerCase() : header + ) : []; if (requestedAttributes.filter(attr => attr != 'RestoreStatus').length > 0) { requestContexts.push(generateRequestContext('listObjectsV2OptionalAttributes')); From c69a4d3acee8c11d8dce22063634811ba92f87a6 Mon Sep 17 00:00:00 2001 From: DarkIsDude Date: Wed, 14 Jan 2026 13:53:12 +0100 Subject: [PATCH 08/12] =?UTF-8?q?=E2=9C=A8=20make=20error=20message=20comp?= =?UTF-8?q?liant=20with=20AWS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Issue: CLDSRV-812 --- lib/api/bucketGet.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/api/bucketGet.js b/lib/api/bucketGet.js index 93d03e1141..64b5322fdd 100644 --- a/lib/api/bucketGet.js +++ b/lib/api/bucketGet.js @@ -289,7 +289,7 @@ function bucketGet(authInfo, request, log, callback) { request.headers['x-amz-optional-object-attributes']?.split(',').map(attr => attr.trim()) ?? []; if (optionalAttributes.some(attr => !attr.startsWith('x-amz-meta-') && attr != 'RestoreStatus')) { return callback( - errorInstances.InvalidArgument.customizeDescription('Invalid header x-amz-optional-object-attributes') + errorInstances.InvalidArgument.customizeDescription('Invalid attribute name specified') ); } From 193c555d9ee5f3509025083661cca55411e16bf7 Mon Sep 17 00:00:00 2001 From: DarkIsDude Date: Wed, 14 Jan 2026 14:11:33 +0100 Subject: [PATCH 09/12] =?UTF-8?q?=E2=9C=85=20move=20tests=20to=20prepareRe?= =?UTF-8?q?questContexts=20to=20be=20more=20exhaustif?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Issue: CLDSRV-812 --- .../authorization/prepareRequestContexts.js | 5 +- .../authorization/prepareRequestContexts.js | 48 ++++++++++++++++ tests/unit/api/bucketGet.js | 56 +------------------ 3 files changed, 50 insertions(+), 59 deletions(-) diff --git a/lib/api/apiUtils/authorization/prepareRequestContexts.js b/lib/api/apiUtils/authorization/prepareRequestContexts.js index 8516cd95f6..c62ace060a 100644 --- a/lib/api/apiUtils/authorization/prepareRequestContexts.js +++ b/lib/api/apiUtils/authorization/prepareRequestContexts.js @@ -256,10 +256,7 @@ function prepareRequestContexts(apiMethod, request, sourceBucket, requestContexts.push(generateRequestContext(apiMethodAfterVersionCheck)); const optionalAttributesHeader = request.headers['x-amz-optional-object-attributes']; - const requestedAttributes = optionalAttributesHeader ? - optionalAttributesHeader.split(',').map( - header => header !== 'RestoreStatus' ? header.toLowerCase() : header - ) : []; + const requestedAttributes = optionalAttributesHeader ? optionalAttributesHeader.split(',') : []; if (requestedAttributes.filter(attr => attr != 'RestoreStatus').length > 0) { requestContexts.push(generateRequestContext('listObjectsV2OptionalAttributes')); diff --git a/tests/unit/api/apiUtils/authorization/prepareRequestContexts.js b/tests/unit/api/apiUtils/authorization/prepareRequestContexts.js index 6cf2e3c591..0a08b104f3 100644 --- a/tests/unit/api/apiUtils/authorization/prepareRequestContexts.js +++ b/tests/unit/api/apiUtils/authorization/prepareRequestContexts.js @@ -348,4 +348,52 @@ describe('prepareRequestContexts', () => { assert.strictEqual(results[0].getAction(), expectedAction); }); }); + + describe('bucketGet', () => { + describe('x-amz-optional-object-attributes header', () => { + it('should request for specific permission if the header is set', () => { + const apiMethod = 'bucketGet'; + const request = makeRequest({ + 'x-amz-optional-object-attributes': 'x-amz-meta-department', + }); + const results = prepareRequestContexts(apiMethod, request, sourceBucket, sourceObject, sourceVersionId); + + assert.strictEqual(results.length, 2); + assert.strictEqual(results[0].getAction(), 's3:ListBucket'); + assert.strictEqual(results[1].getAction(), 'scality:ListBucketOptionalObjectAttributes'); + }); + + it('should request for specific permission if the header is set with multiple value', () => { + const apiMethod = 'bucketGet'; + const request = makeRequest({ + 'x-amz-optional-object-attributes': 'x-amz-meta-department,RestoreStatus', + }); + const results = prepareRequestContexts(apiMethod, request, sourceBucket, sourceObject, sourceVersionId); + + assert.strictEqual(results.length, 2); + assert.strictEqual(results[0].getAction(), 's3:ListBucket'); + assert.strictEqual(results[1].getAction(), 'scality:ListBucketOptionalObjectAttributes'); + }); + + it('should not request permission if the header contains only RestoreStatus', () => { + const apiMethod = 'bucketGet'; + const request = makeRequest({ + 'x-amz-optional-object-attributes': 'RestoreStatus', + }); + const results = prepareRequestContexts(apiMethod, request, sourceBucket, sourceObject, sourceVersionId); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].getAction(), 's3:ListBucket'); + }); + + it('should not request permission if the header does not exists', () => { + const apiMethod = 'bucketGet'; + const request = makeRequest({}); + const results = prepareRequestContexts(apiMethod, request, sourceBucket, sourceObject, sourceVersionId); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].getAction(), 's3:ListBucket'); + }); + }); + }); }); diff --git a/tests/unit/api/bucketGet.js b/tests/unit/api/bucketGet.js index 2499f3d8af..48befeafef 100644 --- a/tests/unit/api/bucketGet.js +++ b/tests/unit/api/bucketGet.js @@ -334,61 +334,7 @@ describe('bucketGet API V2', () => { }); }); - describe('x-amz-optional-attributes header', () => { - it('should return an error if the user does not have the permission', done => { - const authInfoNoPerm = makeAuthInfo('accessKey2'); // A new user without specific permissions - const testGetRequest = Object.assign({ - query: {}, - url: baseUrl, - }, baseGetRequest); - testGetRequest.headers['x-amz-optional-object-attributes'] = 'x-amz-meta-department'; - - async.series([ - next => bucketPut(authInfo, testPutBucketRequest, log, next), - next => bucketGet(authInfoNoPerm, testGetRequest, log, next), - ], err => { - assert.strictEqual(err.is.AccessDenied, true); - done(); - }); - }); - - it('should pass without permission if the header contains only RestoreStatus', done => { - const testGetRequest = Object.assign({ - query: {}, - url: baseUrl, - }, baseGetRequest); - testGetRequest.headers['x-amz-optional-object-attributes'] = 'RestoreStatus'; - - async.waterfall([ - next => bucketPut(authInfo, testPutBucketRequest, log, next), - (_, next) => bucketGet(authInfo, testGetRequest, log, next), - (result, _, next) => parseString(result, next), - ], (err, result) => { - assert.strictEqual(err, null); - assert.strictEqual(result.ListBucketResult.$.xmlns, 'http://s3.amazonaws.com/doc/2006-03-01/'); - done(); - }); - }); - - it('should return valid xml if the user have the permission', done => { - const testGetRequest = Object.assign({ - query: {}, - url: baseUrl, - }, baseGetRequest); - testGetRequest.headers['x-amz-optional-object-attributes'] = 'x-amz-meta-department'; - - async.waterfall([ - next => bucketPut(authInfo, testPutBucketRequest, log, next), - (_, next) => bucketGet(authInfo, testGetRequest, log, next), - (result, _, next) => parseString(result, next), - ], - (err, result) => { - assert.strictEqual(err, null); - assert.strictEqual(result.ListBucketResult.$.xmlns, 'http://s3.amazonaws.com/doc/2006-03-01/'); - done(); - }); - }); - + describe('x-amz-optional-object-attributes header', () => { it('should return an error if the header is empty', done => { const testGetRequest = Object.assign({ query: {}, From 8c3a8105a7c1541f0f0eeba71fb9a981792fba1e Mon Sep 17 00:00:00 2001 From: DarkIsDude Date: Thu, 8 Jan 2026 17:00:53 +0100 Subject: [PATCH 10/12] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20bump=20arsenal?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Issue: CLDSRV-813 --- package.json | 2 +- yarn.lock | 54 ++++++++++++++++++++++++++-------------------------- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/package.json b/package.json index a323c66efd..df84ef014b 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "dependencies": { "@azure/storage-blob": "^12.28.0", "@hapi/joi": "^17.1.1", - "arsenal": "git+https://github.com/scality/Arsenal#8.2.39", + "arsenal": "git+https://github.com/scality/arsenal#8.2.44", "async": "2.6.4", "aws-sdk": "^2.1692.0", "bucketclient": "scality/bucketclient#8.2.7", diff --git a/yarn.lock b/yarn.lock index 6a7621b45b..2dfce61f24 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1451,32 +1451,37 @@ arraybuffer.prototype.slice@^1.0.4: optionalDependencies: ioctl "^2.0.2" -"arsenal@git+https://github.com/scality/Arsenal#8.2.39": - version "8.2.39" - resolved "git+https://github.com/scality/Arsenal#c81b221b6a1e6f283ae74a9e4cd791165adcaedb" +"arsenal@git+https://github.com/scality/Arsenal#8.2.4": + version "8.2.4" + resolved "git+https://github.com/scality/Arsenal#96ef6a3e26d7528f877300606586759f1da6d0cd" dependencies: - "@azure/identity" "^4.13.0" - "@azure/storage-blob" "^12.28.0" + "@azure/identity" "^4.5.0" + "@azure/storage-blob" "^12.25.0" + "@eslint/plugin-kit" "^0.2.3" "@js-sdsl/ordered-set" "^4.4.2" "@scality/hdclient" "^1.3.1" + "@types/async" "^3.2.24" + "@types/utf8" "^3.0.3" JSONStream "^1.3.5" - agentkeepalive "^4.6.0" + agentkeepalive "^4.5.0" ajv "6.12.3" async "~2.6.4" aws-sdk "^2.1691.0" backo "^1.1.0" base-x "3.0.8" base62 "^2.0.2" - debug "^4.4.3" + bson "^6.8.0" + debug "^4.3.7" + diskusage "^1.2.0" fcntl "github:scality/node-fcntl#0.3.0" httpagent scality/httpagent#1.1.0 - https-proxy-agent "^7.0.6" - ioredis "^5.8.1" + https-proxy-agent "^7.0.5" + ioredis "^5.4.1" ipaddr.js "^2.2.0" - joi "^18.0.1" + joi "^17.13.3" level "~5.0.1" level-sublevel "~6.6.5" - mongodb "^6.20.0" + mongodb "^6.11.0" node-forge "^1.3.1" prom-client "^15.1.3" simple-glob "^0.2.0" @@ -1490,37 +1495,32 @@ arraybuffer.prototype.slice@^1.0.4: optionalDependencies: ioctl "^2.0.2" -"arsenal@git+https://github.com/scality/Arsenal#8.2.4": - version "8.2.4" - resolved "git+https://github.com/scality/Arsenal#96ef6a3e26d7528f877300606586759f1da6d0cd" +"arsenal@git+https://github.com/scality/arsenal#8.2.44": + version "8.2.44" + resolved "git+https://github.com/scality/arsenal#960e77028b6eb614dde297e50a1530dcd9015f16" dependencies: - "@azure/identity" "^4.5.0" - "@azure/storage-blob" "^12.25.0" - "@eslint/plugin-kit" "^0.2.3" + "@azure/identity" "^4.13.0" + "@azure/storage-blob" "^12.28.0" "@js-sdsl/ordered-set" "^4.4.2" "@scality/hdclient" "^1.3.1" - "@types/async" "^3.2.24" - "@types/utf8" "^3.0.3" JSONStream "^1.3.5" - agentkeepalive "^4.5.0" + agentkeepalive "^4.6.0" ajv "6.12.3" async "~2.6.4" aws-sdk "^2.1691.0" backo "^1.1.0" base-x "3.0.8" base62 "^2.0.2" - bson "^6.8.0" - debug "^4.3.7" - diskusage "^1.2.0" + debug "^4.4.3" fcntl "github:scality/node-fcntl#0.3.0" httpagent scality/httpagent#1.1.0 - https-proxy-agent "^7.0.5" - ioredis "^5.4.1" + https-proxy-agent "^7.0.6" + ioredis "^5.8.1" ipaddr.js "^2.2.0" - joi "^17.13.3" + joi "^18.0.1" level "~5.0.1" level-sublevel "~6.6.5" - mongodb "^6.11.0" + mongodb "^6.20.0" node-forge "^1.3.1" prom-client "^15.1.3" simple-glob "^0.2.0" From 8e1040d7c13f6d2f311dc07981ccd228190c9534 Mon Sep 17 00:00:00 2001 From: DarkIsDude Date: Fri, 9 Jan 2026 11:27:07 +0100 Subject: [PATCH 11/12] =?UTF-8?q?=E2=9C=85=20ignore=20restoreStatus=20in?= =?UTF-8?q?=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Issue: CLDSRV-813 --- .../aws-node-sdk/test/object/mpuVersion.js | 47 ++++++++-------- .../aws-node-sdk/test/object/putVersion.js | 55 ++++++++++++------- 2 files changed, 59 insertions(+), 43 deletions(-) diff --git a/tests/functional/aws-node-sdk/test/object/mpuVersion.js b/tests/functional/aws-node-sdk/test/object/mpuVersion.js index 0a89eb7e32..e801a3910c 100644 --- a/tests/functional/aws-node-sdk/test/object/mpuVersion.js +++ b/tests/functional/aws-node-sdk/test/object/mpuVersion.js @@ -101,7 +101,7 @@ function checkObjMdAndUpdate(objMDBefore, objMDAfter, props) { }); } -function clearUploadIdFromVersions(versions) { +function clearUploadIdFromVersionsAndRestoreStatus(versions) { if (!versions || versions.length === 0) { return versions; } @@ -109,6 +109,7 @@ function clearUploadIdFromVersions(versions) { for (const version of versions) { if (version.value) { version.value.uploadId = undefined; + version.value.restoreStatus = undefined; } } @@ -263,7 +264,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = clearUploadIdFromVersions(res.Versions); + versionsBefore = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); return next(err); }), next => putMPUVersion(s3, bucketName, objectName, '', next), @@ -272,7 +273,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = clearUploadIdFromVersions(res.Versions); + versionsAfter = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); return next(err); }), ], err => { @@ -304,7 +305,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = clearUploadIdFromVersions(res.Versions); + versionsBefore = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); return next(err); }), next => putMPUVersion(s3, bucketName, objectName, '', next), @@ -313,7 +314,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = clearUploadIdFromVersions(res.Versions); + versionsAfter = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); return next(err); }), ], err => { @@ -353,7 +354,7 @@ describe('MPU with x-scal-s3-version-id header', () => { }), next => fakeMetadataArchive(bucketName, objectName, vId, archive, next), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = clearUploadIdFromVersions(res.Versions); + versionsBefore = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); return next(err); }), next => getMetadata(bucketName, objectName, vId, (err, objMD) => { @@ -366,7 +367,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = clearUploadIdFromVersions(res.Versions); + versionsAfter = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); return next(err); }), ], err => { @@ -405,7 +406,7 @@ describe('MPU with x-scal-s3-version-id header', () => { }), next => fakeMetadataArchive(bucketName, objectName, vId, archive, next), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = clearUploadIdFromVersions(res.Versions); + versionsBefore = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); return next(err); }), next => getMetadata(bucketName, objectName, vId, (err, objMD) => { @@ -418,7 +419,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = clearUploadIdFromVersions(res.Versions); + versionsAfter = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); return next(err); }), ], err => { @@ -458,7 +459,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = clearUploadIdFromVersions(res.Versions); + versionsBefore = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); return next(err); }), next => putMPUVersion(s3, bucketName, objectName, 'null', next), @@ -467,7 +468,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = clearUploadIdFromVersions(res.Versions); + versionsAfter = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); return next(err); }), ], err => { @@ -511,7 +512,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = clearUploadIdFromVersions(res.Versions); + versionsBefore = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); next(err); }), next => putMPUVersion(s3, bucketName, objectName, vId, next), @@ -520,7 +521,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = clearUploadIdFromVersions(res.Versions); + versionsAfter = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); return next(err); }), ], err => { @@ -567,7 +568,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = clearUploadIdFromVersions(res.Versions); + versionsBefore = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); next(err); }), next => putMPUVersion(s3, bucketName, objectName, '', next), @@ -576,7 +577,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = clearUploadIdFromVersions(res.Versions); + versionsAfter = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); return next(err); }), ], err => { @@ -621,7 +622,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = clearUploadIdFromVersions(res.Versions); + versionsBefore = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); return next(err); }), next => putMPUVersion(s3, bucketName, objectName, vId, next), @@ -630,7 +631,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = clearUploadIdFromVersions(res.Versions); + versionsAfter = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); return next(err); }), ], err => { @@ -670,7 +671,7 @@ describe('MPU with x-scal-s3-version-id header', () => { }), next => fakeMetadataArchive(bucketName, objectName, vId, archive, next), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = clearUploadIdFromVersions(res.Versions); + versionsBefore = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); return next(err); }), next => getMetadata(bucketName, objectName, vId, (err, objMD) => { @@ -683,7 +684,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = clearUploadIdFromVersions(res.Versions); + versionsAfter = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); return next(err); }), ], err => { @@ -729,7 +730,7 @@ describe('MPU with x-scal-s3-version-id header', () => { }), next => fakeMetadataArchive(bucketName, objectName, vId, archive, next), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = clearUploadIdFromVersions(res.Versions); + versionsBefore = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); return next(err); }), next => getMetadata(bucketName, objectName, vId, (err, objMD) => { @@ -743,7 +744,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = clearUploadIdFromVersions(res.Versions); + versionsAfter = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); return next(err); }), ], err => { @@ -777,7 +778,7 @@ describe('MPU with x-scal-s3-version-id header', () => { next => s3.putObject(params, next), next => fakeMetadataArchive(bucketName, objectName, undefined, archive, next), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = clearUploadIdFromVersions(res.Versions); + versionsBefore = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); return next(err); }), next => getMetadata(bucketName, objectName, undefined, (err, objMD) => { @@ -791,7 +792,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = clearUploadIdFromVersions(res.Versions); + versionsAfter = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); return next(err); }), ], err => { diff --git a/tests/functional/aws-node-sdk/test/object/putVersion.js b/tests/functional/aws-node-sdk/test/object/putVersion.js index f90d712536..74dc164ff9 100644 --- a/tests/functional/aws-node-sdk/test/object/putVersion.js +++ b/tests/functional/aws-node-sdk/test/object/putVersion.js @@ -34,6 +34,21 @@ function putObjectVersion(s3, params, vid, next) { return request.send(next); } +function clearRestoreStatus(versions) { + if (!versions || versions.length === 0) { + return versions; + } + + for (const version of versions) { + if (version.value) { + version.value.restoreStatus = undefined; + } + } + + return versions; +} + + function checkVersionsAndUpdate(versionsBefore, versionsAfter, indexes) { indexes.forEach(i => { assert.notStrictEqual(versionsAfter[i].value.Size, versionsBefore[i].value.Size); @@ -203,7 +218,7 @@ describe('PUT object with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = res.Versions; + versionsBefore = clearRestoreStatus(res.Versions); return next(err); }), next => putObjectVersion(s3, params, '', next), @@ -212,7 +227,7 @@ describe('PUT object with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = res.Versions; + versionsAfter = clearRestoreStatus(res.Versions); return next(err); }), ], err => { @@ -250,7 +265,7 @@ describe('PUT object with x-scal-s3-version-id header', () => { }), next => fakeMetadataArchive(bucketName, objectName, vId, archive, next), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = res.Versions; + versionsBefore = clearRestoreStatus(res.Versions); return next(err); }), next => getMetadata(bucketName, objectName, vId, (err, objMD) => { @@ -263,7 +278,7 @@ describe('PUT object with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = res.Versions; + versionsAfter = clearRestoreStatus(res.Versions); return next(err); }), ], err => { @@ -301,7 +316,7 @@ describe('PUT object with x-scal-s3-version-id header', () => { }), next => fakeMetadataArchive(bucketName, objectName, vId, archive, next), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = res.Versions; + versionsBefore = clearRestoreStatus(res.Versions); return next(err); }), next => getMetadata(bucketName, objectName, vId, (err, objMD) => { @@ -314,7 +329,7 @@ describe('PUT object with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = res.Versions; + versionsAfter = clearRestoreStatus(res.Versions); return next(err); }), ], err => { @@ -353,7 +368,7 @@ describe('PUT object with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = res.Versions; + versionsBefore = clearRestoreStatus(res.Versions); next(err); }), next => putObjectVersion(s3, params, 'null', next), @@ -362,7 +377,7 @@ describe('PUT object with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = res.Versions; + versionsAfter = clearRestoreStatus(res.Versions); return next(err); }), ], err => { @@ -405,7 +420,7 @@ describe('PUT object with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = res.Versions; + versionsBefore = clearRestoreStatus(res.Versions); return next(err); }), next => putObjectVersion(s3, params, vId, next), @@ -414,7 +429,7 @@ describe('PUT object with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = res.Versions; + versionsAfter = clearRestoreStatus(res.Versions); return next(err); }), ], err => { @@ -460,7 +475,7 @@ describe('PUT object with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = res.Versions; + versionsBefore = clearRestoreStatus(res.Versions); return next(err); }), next => putObjectVersion(s3, params, '', next), @@ -469,7 +484,7 @@ describe('PUT object with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = res.Versions; + versionsAfter = clearRestoreStatus(res.Versions); return next(err); }), ], err => { @@ -509,7 +524,7 @@ describe('PUT object with x-scal-s3-version-id header', () => { next => fakeMetadataArchive(bucketName, objectName, vId, archive, next), next => s3.putObject(params, next), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = res.Versions; + versionsBefore = clearRestoreStatus(res.Versions); return next(err); }), next => getMetadata(bucketName, objectName, vId, (err, objMD) => { @@ -522,7 +537,7 @@ describe('PUT object with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = res.Versions; + versionsAfter = clearRestoreStatus(res.Versions); return next(err); }), ], err => { @@ -561,7 +576,7 @@ describe('PUT object with x-scal-s3-version-id header', () => { }), next => fakeMetadataArchive(bucketName, objectName, vId, archive, next), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = res.Versions; + versionsBefore = clearRestoreStatus(res.Versions); return next(err); }), next => getMetadata(bucketName, objectName, vId, (err, objMD) => { @@ -574,7 +589,7 @@ describe('PUT object with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = res.Versions; + versionsAfter = clearRestoreStatus(res.Versions); return next(err); }), ], err => { @@ -619,7 +634,7 @@ describe('PUT object with x-scal-s3-version-id header', () => { }), next => fakeMetadataArchive(bucketName, objectName, vId, archive, next), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = res.Versions; + versionsBefore = clearRestoreStatus(res.Versions); return next(err); }), next => getMetadata(bucketName, objectName, vId, (err, objMD) => { @@ -633,7 +648,7 @@ describe('PUT object with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = res.Versions; + versionsAfter = clearRestoreStatus(res.Versions); return next(err); }), ], err => { @@ -666,7 +681,7 @@ describe('PUT object with x-scal-s3-version-id header', () => { next => s3.putObject(params, next), next => fakeMetadataArchive(bucketName, objectName, undefined, archive, next), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = res.Versions; + versionsBefore = clearRestoreStatus(res.Versions); return next(err); }), next => getMetadata(bucketName, objectName, undefined, (err, objMD) => { @@ -680,7 +695,7 @@ describe('PUT object with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = res.Versions; + versionsAfter = clearRestoreStatus(res.Versions); return next(err); }), ], err => { From ccaf4008c0be4ef08b90b5a47d6e82467eef7e71 Mon Sep 17 00:00:00 2001 From: DarkIsDude Date: Tue, 13 Jan 2026 11:32:15 +0100 Subject: [PATCH 12/12] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20rename=20function=20?= =?UTF-8?q?to=20reflect=20properly=20the=20purpose?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Issue: CLDSRV-813 --- .../aws-node-sdk/test/object/mpuVersion.js | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/tests/functional/aws-node-sdk/test/object/mpuVersion.js b/tests/functional/aws-node-sdk/test/object/mpuVersion.js index e801a3910c..012c3d6e9a 100644 --- a/tests/functional/aws-node-sdk/test/object/mpuVersion.js +++ b/tests/functional/aws-node-sdk/test/object/mpuVersion.js @@ -101,7 +101,7 @@ function checkObjMdAndUpdate(objMDBefore, objMDAfter, props) { }); } -function clearUploadIdFromVersionsAndRestoreStatus(versions) { +function clearUploadIdAndRestoreStatusFromVersions(versions) { if (!versions || versions.length === 0) { return versions; } @@ -264,7 +264,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); + versionsBefore = clearUploadIdAndRestoreStatusFromVersions(res.Versions); return next(err); }), next => putMPUVersion(s3, bucketName, objectName, '', next), @@ -273,7 +273,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); + versionsAfter = clearUploadIdAndRestoreStatusFromVersions(res.Versions); return next(err); }), ], err => { @@ -305,7 +305,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); + versionsBefore = clearUploadIdAndRestoreStatusFromVersions(res.Versions); return next(err); }), next => putMPUVersion(s3, bucketName, objectName, '', next), @@ -314,7 +314,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); + versionsAfter = clearUploadIdAndRestoreStatusFromVersions(res.Versions); return next(err); }), ], err => { @@ -354,7 +354,7 @@ describe('MPU with x-scal-s3-version-id header', () => { }), next => fakeMetadataArchive(bucketName, objectName, vId, archive, next), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); + versionsBefore = clearUploadIdAndRestoreStatusFromVersions(res.Versions); return next(err); }), next => getMetadata(bucketName, objectName, vId, (err, objMD) => { @@ -367,7 +367,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); + versionsAfter = clearUploadIdAndRestoreStatusFromVersions(res.Versions); return next(err); }), ], err => { @@ -406,7 +406,7 @@ describe('MPU with x-scal-s3-version-id header', () => { }), next => fakeMetadataArchive(bucketName, objectName, vId, archive, next), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); + versionsBefore = clearUploadIdAndRestoreStatusFromVersions(res.Versions); return next(err); }), next => getMetadata(bucketName, objectName, vId, (err, objMD) => { @@ -419,7 +419,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); + versionsAfter = clearUploadIdAndRestoreStatusFromVersions(res.Versions); return next(err); }), ], err => { @@ -459,7 +459,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); + versionsBefore = clearUploadIdAndRestoreStatusFromVersions(res.Versions); return next(err); }), next => putMPUVersion(s3, bucketName, objectName, 'null', next), @@ -468,7 +468,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); + versionsAfter = clearUploadIdAndRestoreStatusFromVersions(res.Versions); return next(err); }), ], err => { @@ -512,7 +512,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); + versionsBefore = clearUploadIdAndRestoreStatusFromVersions(res.Versions); next(err); }), next => putMPUVersion(s3, bucketName, objectName, vId, next), @@ -521,7 +521,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); + versionsAfter = clearUploadIdAndRestoreStatusFromVersions(res.Versions); return next(err); }), ], err => { @@ -568,7 +568,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); + versionsBefore = clearUploadIdAndRestoreStatusFromVersions(res.Versions); next(err); }), next => putMPUVersion(s3, bucketName, objectName, '', next), @@ -577,7 +577,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); + versionsAfter = clearUploadIdAndRestoreStatusFromVersions(res.Versions); return next(err); }), ], err => { @@ -622,7 +622,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); + versionsBefore = clearUploadIdAndRestoreStatusFromVersions(res.Versions); return next(err); }), next => putMPUVersion(s3, bucketName, objectName, vId, next), @@ -631,7 +631,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); + versionsAfter = clearUploadIdAndRestoreStatusFromVersions(res.Versions); return next(err); }), ], err => { @@ -671,7 +671,7 @@ describe('MPU with x-scal-s3-version-id header', () => { }), next => fakeMetadataArchive(bucketName, objectName, vId, archive, next), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); + versionsBefore = clearUploadIdAndRestoreStatusFromVersions(res.Versions); return next(err); }), next => getMetadata(bucketName, objectName, vId, (err, objMD) => { @@ -684,7 +684,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); + versionsAfter = clearUploadIdAndRestoreStatusFromVersions(res.Versions); return next(err); }), ], err => { @@ -730,7 +730,7 @@ describe('MPU with x-scal-s3-version-id header', () => { }), next => fakeMetadataArchive(bucketName, objectName, vId, archive, next), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); + versionsBefore = clearUploadIdAndRestoreStatusFromVersions(res.Versions); return next(err); }), next => getMetadata(bucketName, objectName, vId, (err, objMD) => { @@ -744,7 +744,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); + versionsAfter = clearUploadIdAndRestoreStatusFromVersions(res.Versions); return next(err); }), ], err => { @@ -778,7 +778,7 @@ describe('MPU with x-scal-s3-version-id header', () => { next => s3.putObject(params, next), next => fakeMetadataArchive(bucketName, objectName, undefined, archive, next), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); + versionsBefore = clearUploadIdAndRestoreStatusFromVersions(res.Versions); return next(err); }), next => getMetadata(bucketName, objectName, undefined, (err, objMD) => { @@ -792,7 +792,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); + versionsAfter = clearUploadIdAndRestoreStatusFromVersions(res.Versions); return next(err); }), ], err => {