Skip to content

Conversation

@shirady
Copy link
Contributor

@shirady shirady commented Nov 24, 2025

Describe the Problem

Currently, in Containerised NooBaa deployment, when a user is created under an account (and has access keys), they must have a matching IAM policy to perform any S3 operations.

Explain the Changes

  1. Edit the error message when a user tries to perform an S3 operation to match what it tries to do and have a proper message when executing authorize_request_iam_policy.
  2. Add log messages when there is no IAM user policy for better experience.
  3. Fix a minor bug when I read the code so I changed the path to be iam_path according to the account schemas in NC and Containerised deployment.
  4. Fix the resource_arn in the IAM policy for the list buckets operation.

Issues:

List of GAPs:

  1. The tests are only manual at this point; there is a plan to add automated tests after we have the design change.
  2. I might want to refactor the code so that s3_bucket_policy_utils.js so the terms will be policy in general and can be used for bucket policy and IAM policy.
  3. I might want to move the created function somewhere else.

Testing Instructions:

  1. Build the images and install NooBaa system on Rancher Desktop (see guide).
    Note: nb is an alias that runs the local operator from build/_output/bin (alias created by devenv).
  2. Wait for the default backing store pod to be in state Ready before starting the tests: kubectl wait --for=condition=available backingstore/noobaa-default-backing-store --timeout=6m -n test1
  3. I'm using port-forward (in a different tab):
  • S3 kubectl port-forward -n test1 service/s3 12443:443
  • IAM kubectl port-forward -n test1 service/iam 14443:443
  1. Create the alias for the admin - first, need to get the credentials: nb status --show-secrets -n test1 and then:
    alias admin-s3='AWS_ACCESS_KEY=<access-key> AWS_SECRET_ACCESS_KEY=<secret-key> aws --no-verify-ssl --endpoint-url https://localhost:12443'
    alias admin-iam='AWS_ACCESS_KEY=<access-key> AWS_SECRET_ACCESS_KEY=<secret-key> aws --no-verify-ssl --endpoint-url https://localhost:14443'
  2. Check the connection to the endpoint:
  • try to list the users (should be an empty list): admin-iam iam list-users; echo $?
  • try to list the buckets (should be first.bucket): admin-s3 s3 ls; echo $?
  1. Create a user: admin-iam iam create-user --user-name Robert
    Note: To validate user creation, you can rerun admin-iam iam list-users and expect 1 user in the list
  2. Create access keys: admin-iam iam create-access-keys --user-name Robert
  3. Create the alias for the user (like in step 4 with alias user-1-s3): alias user-1-s3='AWS_ACCESS_KEY=<access-key> AWS_SECRET_ACCESS_KEY=<secret-key> aws --no-verify-ssl --endpoint-url https://localhost:12443'
  4. The case with no bucket policy, no IAM policy - the user would be restricted from S3 operation, for example:
  • The user lists objects in a bucket: user-1-s3 s3 ls s3://first.bucket (should not work - should throw AccessDenied error - look at the error). Example:
An error occurred (AccessDenied) when calling the ListObjectsV2 operation: User: arn:aws:iam::6922dfb199663d002117094e:user/Robert is not authorized to perform: s3:ListBucket on resource: arn:aws:s3:::first.bucket because no identity-based policy allows the s3:ListBucket action
  1. The admin adds an inline policy so the user can list objects in the bucket.
    policy_allow_list_bucket.json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:ListBucket"
            ],
            "Resource": "*"
        }
    ]
}
  1. admin-iam iam put-user-policy --user-name Robert --policy-name policy_allow_list_bucket --policy-document file://policy_allow_list_bucket.json
  2. Repeat step 9:
  • The user lists objects in a bucket: user-1-s3 s3 ls s3://first.bucket (should work - bucket is empty, no error was thrown).

Code changes for testing:

  1. To see the account (of a user) in the cache after changes, src/sdk/object_sdk.js uses cache expiry of 1 millisecond.
const account_cache = new LRUCache({
    name: 'AccountCache',
-    expiry_ms: config.OBJECT_SDK_ACCOUNT_CACHE_EXPIRY_MS,
+   expiry_ms: 1, //SDSD 

Notes:

  • In step 1 - deploying the system, I used --use-standalone-db for simplicity (fewer steps for the system in Ready status).

  • I used the admin account, but for IAM operations, there is no difference between a created account and an admin account. I tested with the admin only because it saves steps (I have the admin account upon system creation and a bucket first.bucket).

  • Doc added/updated

  • Tests added

Summary by CodeRabbit

  • Bug Fixes
    • More detailed "Access Denied" messages for S3 operations with clearer account and resource context.
    • Consistent account-path formatting in authorization error messages for clearer diagnostics.
    • Improved handling when the resource/bucket is unspecified (e.g., list-all-buckets), producing accurate denial messages.
    • Unified error logging and reporting when inline policies deny or no policies match.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Nov 24, 2025

Walkthrough

Adds an IAM helper to compose detailed S3 "not authorized" messages, exports it, integrates it into the S3 IAM authorization flow to produce detailed AccessDenied errors (defaults resource ARN to '*' if undeterminable), and switches requester ARN construction to use requesting_account.iam_path.

Changes

Cohort / File(s) Summary
IAM helper added & exported
src/endpoint/iam/iam_utils.js
Added _create_detailed_message_for_iam_user_access_in_s3(user_account, method, resource_arn) to build detailed S3 access-denied messages (resolves action from method, handles array-form methods and get_object_attributes, omits resource for s3:ListAllMyBuckets) and exported it.
S3 authorization -> use detailed IAM errors
src/endpoint/s3/s3_rest.js
Imported the new helper; authorize_request_iam_policy() now falls back to '*' when _get_arn_from_req_path(req) is undefined, avoids constructing invalid ARNs when bucket missing, and uses _throw_iam_access_denied_error_for_s3_operation to log and throw detailed AccessDenied errors for empty inline policies (non-NC), DENY, and no-matching-policy cases. Minor comment/alignment tweaks.
ARN construction for error messages updated
src/sdk/accountspace_fs.js, src/util/account_util.js
Changed requester ARN construction to use requesting_account.iam_path (with IAM default fallback) instead of requesting_account.path, affecting formatted principal ARNs in error messages.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant S3 as s3_rest.js
    participant IAM as iam_utils.js

    Client->>S3: Send S3 request
    S3->>S3: authorize_request_iam_policy(req)
    alt bucket present
        S3->>S3: resource_arn = _get_arn_from_req_path(req)
    else bucket missing / cannot derive
        S3->>S3: resource_arn = '*'
    end
    S3->>S3: determine requesting_account (use iam_path)

    alt inline policies present
        S3->>S3: evaluate inline policies
        alt any policy DENY
            S3->>IAM: _create_detailed_message_for_iam_user_access_in_s3(requesting_account, method, resource_arn)
            IAM-->>S3: detailed message
            S3->>S3: _throw_iam_access_denied_error_for_s3_operation(...) -> throw AccessDenied
        else no policy matches
            S3->>IAM: _create_detailed_message_for_iam_user_access_in_s3(...)
            IAM-->>S3: detailed message
            S3->>S3: throw AccessDenied with detailed message
        end
    else no inline policies
        alt NC deployment
            S3->>S3: return early (no-op)
        else
            S3->>IAM: _create_detailed_message_for_iam_user_access_in_s3(...)
            IAM-->>S3: detailed message
            S3->>S3: throw AccessDenied with detailed message
        end
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Review src/endpoint/s3/s3_rest.js for correct fallback to '*', early-return when bucket missing, and correct use of the new throw helper in all failing branches.
  • Verify action resolution and special-case logic in src/endpoint/iam/iam_utils.js (array-form methods, get_object_attributes, s3:ListAllMyBuckets omission).
  • Confirm ARN construction changes in src/util/account_util.js and src/sdk/accountspace_fs.js use iam_path with the proper fallback.

Possibly related PRs

Suggested reviewers

  • aayushchouhan09
  • naveenpaul1
  • liranmauda

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main changes: improving IAM error messages for users without IAM user policies. It directly addresses the core objective of the PR.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@shirady shirady force-pushed the iam-user-policy-error-message branch from d1bff60 to a6ae534 Compare November 24, 2025 10:58
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/endpoint/s3/s3_rest.js (1)

17-18: IAM S3 authorization wiring and detailed AccessDenied look consistent

The new _throw_iam_access_denied_error_for_s3_operation plus _create_detailed_message_for_access_in_s3 integration cleanly upgrades IAM-based S3 AccessDenied errors with action and resource context, and the NC skip (nsfs_config_root) avoids enforcing non-existent IAM policies there.

One behavioral aspect to double‑check: for non‑NC deployments, IAM users without matching/any inline IAM policies are denied here before bucket policies are evaluated, so a bucket policy alone cannot grant access. If the goal is to model AWS semantics where resource policies can allow access even when identity policies don’t, you may want to revisit this precedence; if stricter IAM‑first behavior is intentional, the current logic is fine.

Also applies to: 334-381

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2af53d0 and d1bff60.

📒 Files selected for processing (4)
  • src/endpoint/iam/iam_utils.js (2 hunks)
  • src/endpoint/s3/s3_rest.js (5 hunks)
  • src/sdk/accountspace_fs.js (1 hunks)
  • src/util/account_util.js (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: naveenpaul1
Repo: noobaa/noobaa-core PR: 9277
File: src/endpoint/s3/s3_rest.js:258-261
Timestamp: 2025-11-12T04:55:42.193Z
Learning: In the context of S3 REST requests (src/endpoint/s3/s3_rest.js), the account.owner field from req.object_sdk.requesting_account is already a string (account ID) because it comes from RPC serialization where owner._id.toString() is applied in account_server.js. No additional .toString() or ._id extraction is needed when passing account.owner to IAM utility functions.
📚 Learning: 2025-11-12T04:55:42.193Z
Learnt from: naveenpaul1
Repo: noobaa/noobaa-core PR: 9277
File: src/endpoint/s3/s3_rest.js:258-261
Timestamp: 2025-11-12T04:55:42.193Z
Learning: In the context of S3 REST requests (src/endpoint/s3/s3_rest.js), the account.owner field from req.object_sdk.requesting_account is already a string (account ID) because it comes from RPC serialization where owner._id.toString() is applied in account_server.js. No additional .toString() or ._id extraction is needed when passing account.owner to IAM utility functions.

Applied to files:

  • src/endpoint/iam/iam_utils.js
  • src/util/account_util.js
  • src/sdk/accountspace_fs.js
  • src/endpoint/s3/s3_rest.js
🧬 Code graph analysis (3)
src/endpoint/iam/iam_utils.js (3)
src/endpoint/s3/s3_rest.js (6)
  • requesting_account (346-346)
  • method (243-243)
  • method (345-345)
  • method (484-484)
  • resource_arn (344-344)
  • message_with_details (378-378)
src/endpoint/iam/iam_rest.js (1)
  • method (229-229)
src/util/account_util.js (8)
  • arn_for_requesting_account (448-449)
  • full_action_name (446-446)
  • message_with_details (328-328)
  • message_with_details (365-365)
  • message_with_details (393-393)
  • message_with_details (429-429)
  • message_with_details (435-435)
  • message_with_details (441-441)
src/util/account_util.js (3)
src/server/system_services/account_server.js (16)
  • requesting_account (1198-1198)
  • requesting_account (1218-1218)
  • requesting_account (1260-1260)
  • requesting_account (1277-1277)
  • requesting_account (1305-1305)
  • requesting_account (1336-1336)
  • requesting_account (1349-1349)
  • requesting_account (1377-1377)
  • requesting_account (1394-1394)
  • requesting_account (1415-1415)
  • requesting_account (1457-1457)
  • requesting_account (1486-1486)
  • requesting_account (1516-1516)
  • requesting_account (1545-1545)
  • requesting_account (1559-1559)
  • requesting_account (1578-1578)
src/endpoint/s3/s3_bucket_policy_utils.js (1)
  • IAM_DEFAULT_PATH (97-97)
src/endpoint/iam/iam_constants.js (1)
  • IAM_DEFAULT_PATH (69-69)
src/sdk/accountspace_fs.js (1)
src/endpoint/s3/s3_rest.js (1)
  • requesting_account (346-346)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: run-package-lock-validation
  • GitHub Check: Build Noobaa Image
  • GitHub Check: run-jest-unit-tests
🔇 Additional comments (4)
src/sdk/accountspace_fs.js (1)

672-695: Access-denied ARN now correctly respects IAM path

Using requesting_account.iam_path when building arn_for_requesting_account makes the error message consistent with IAM path semantics (and with _create_arn_for_account_or_user). create_arn_for_user already handles missing/default paths, so this is a safe behavioral improvement.

src/util/account_util.js (1)

445-467: IAM error ARN construction aligned with iam_path

Switching to requesting_account.iam_path || IAM_DEFAULT_PATH when calling create_arn_for_user makes the IAM access-denied message’s ARN consistent with the configured IAM path and provides a sane fallback for legacy accounts.

src/endpoint/iam/iam_utils.js (1)

69-93: S3 IAM denial message helper is coherent and AWS‑like

_create_detailed_message_for_access_in_s3 correctly derives the requester ARN from requesting_account.owner and IAM username, handles both string/array methods (with a special case for ListAllMyBuckets), and produces the expected “no identity-based policy allows …” wording. Export wiring looks good, and using owner directly avoids the S3 owner._id pitfalls noted previously.

Based on learnings, this keeps S3’s requesting_account.owner usage consistent without extra .toString()/._id.

Also applies to: 866-866

src/endpoint/s3/s3_rest.js (1)

412-421: Service‑level S3 ARNs now handled safely

Adding if (!req.params.bucket) return; in _get_arn_from_req_path prevents bogus arn:aws:s3:::undefined values for service‑level operations; combined with the || '*' fallback in authorize_request_iam_policy, this yields a sensible * resource while keeping bucket/object ARNs unchanged.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d1bff60 and a6ae534.

📒 Files selected for processing (4)
  • src/endpoint/iam/iam_utils.js (2 hunks)
  • src/endpoint/s3/s3_rest.js (5 hunks)
  • src/sdk/accountspace_fs.js (1 hunks)
  • src/util/account_util.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/sdk/accountspace_fs.js
  • src/util/account_util.js
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-11-12T04:55:42.193Z
Learnt from: naveenpaul1
Repo: noobaa/noobaa-core PR: 9277
File: src/endpoint/s3/s3_rest.js:258-261
Timestamp: 2025-11-12T04:55:42.193Z
Learning: In the context of S3 REST requests (src/endpoint/s3/s3_rest.js), the account.owner field from req.object_sdk.requesting_account is already a string (account ID) because it comes from RPC serialization where owner._id.toString() is applied in account_server.js. No additional .toString() or ._id extraction is needed when passing account.owner to IAM utility functions.

Applied to files:

  • src/endpoint/iam/iam_utils.js
  • src/endpoint/s3/s3_rest.js
🧬 Code graph analysis (1)
src/endpoint/iam/iam_utils.js (1)
src/endpoint/s3/s3_rest.js (6)
  • requesting_account (346-346)
  • method (243-243)
  • method (345-345)
  • method (484-484)
  • resource_arn (344-344)
  • message_with_details (378-378)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Build Noobaa Image
  • GitHub Check: run-jest-unit-tests
🔇 Additional comments (8)
src/endpoint/iam/iam_utils.js (2)

866-866: LGTM!

The export correctly exposes the new helper function for use by S3 authorization code.


69-93: Now I need to search for where _throw_iam_access_denied_error_for_s3_operation is called to understand the full context:

Based on my verification of the codebase, I can now generate the final response:

Review logic is partially valid but overstated—no actual bug exists in current code.

The array handling on line 80 is fragile but functional:

  1. Single-element array concern: When arrays are embedded in template literals, they're converted via .toString() which returns a comma-separated list without brackets. For a single-element array like ["s3:GetObject"], this would actually produce "s3:GetObject" (identical to the string), so no message corruption occurs. However, no single-element arrays exist in OP_NAME_TO_ACTION—all array values are exactly 2 elements.

  2. Null/undefined concern: The method parameter always comes from _get_method_from_req() in s3_rest.js (line 400-410), which either returns a valid value or throws an error. In practice, method is always defined when this function is called.

  3. Current behavior: The code correctly handles the documented special case (get_object_attributes with 2-element arrays), though it relies on implicit Array.toString() coercion as a fallback.

While the code works, it could be more explicit: either add a type guard (Array.isArray(method) ? method[0] : method) or use explicit type checking instead of relying on array stringification behavior.

src/endpoint/s3/s3_rest.js (6)

17-17: LGTM!

The import correctly brings in the new helper function for building detailed IAM error messages.


348-352: LGTM!

The logic correctly handles the case where IAM users have no inline policies, maintaining backward compatibility for NC deployments while providing detailed error messages for other deployments.


367-367: LGTM!

Using the centralized error message builder for DENY decisions improves consistency and provides better user feedback.


373-375: LGTM!

The logic correctly handles the case where policies exist but none match, providing a detailed error message to help users understand why access was denied.


377-381: LGTM!

The helper function properly encapsulates the logic for throwing detailed IAM access denied errors, promoting code reuse and consistency across authorization paths.


413-413: LGTM!

The early return when no bucket is present prevents undefined ARN construction and enables the fallback to '*' on line 344 for list-all-buckets scenarios.

@shirady shirady self-assigned this Nov 24, 2025
@shirady shirady force-pushed the iam-user-policy-error-message branch from 6f23981 to 7fe2570 Compare November 25, 2025 08:56
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/endpoint/iam/iam_utils.js (1)

69-94: Consider consistent iam_path handling for clarity.

The function correctly builds detailed IAM access messages. However, there's a minor inconsistency: Line 80 uses requesting_account.iam_path directly, while src/util/account_util.js:444 uses requesting_account.iam_path || IAM_DEFAULT_PATH. Although create_arn_for_user handles undefined iam_path internally, using an explicit fallback would improve consistency across the codebase.

Apply this diff for consistency:

 function _create_detailed_message_for_iam_user_access_in_s3(requesting_account, method, resource_arn) {
     const owner_account_id = get_owner_account_id(requesting_account.owner);
+    const { IAM_DEFAULT_PATH } = require('./iam_constants');
     const arn_for_requesting_account = create_arn_for_user(owner_account_id,
-        get_iam_username(requesting_account.name.unwrap()), requesting_account.iam_path);
+        get_iam_username(requesting_account.name.unwrap()), requesting_account.iam_path || IAM_DEFAULT_PATH);
     const full_action_name = Array.isArray(method) && method.length > 1 ? method[1] : method; // special case for get_object_attributes
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6f23981 and 7fe2570.

📒 Files selected for processing (4)
  • src/endpoint/iam/iam_utils.js (2 hunks)
  • src/endpoint/s3/s3_rest.js (5 hunks)
  • src/sdk/accountspace_fs.js (1 hunks)
  • src/util/account_util.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/sdk/accountspace_fs.js
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2025-11-12T04:55:42.193Z
Learnt from: naveenpaul1
Repo: noobaa/noobaa-core PR: 9277
File: src/endpoint/s3/s3_rest.js:258-261
Timestamp: 2025-11-12T04:55:42.193Z
Learning: In the context of S3 REST requests (src/endpoint/s3/s3_rest.js), the account.owner field from req.object_sdk.requesting_account is already a string (account ID) because it comes from RPC serialization where owner._id.toString() is applied in account_server.js. No additional .toString() or ._id extraction is needed when passing account.owner to IAM utility functions.

Applied to files:

  • src/util/account_util.js
  • src/endpoint/iam/iam_utils.js
  • src/endpoint/s3/s3_rest.js
📚 Learning: 2025-11-19T15:03:42.260Z
Learnt from: shirady
Repo: noobaa/noobaa-core PR: 9291
File: src/server/common_services/auth_server.js:548-554
Timestamp: 2025-11-19T15:03:42.260Z
Learning: In src/server/common_services/auth_server.js, account objects are loaded directly from system_store (e.g., system_store.data.get_by_id()), so account.owner is an object ID reference with an ._id property, not a string. This differs from s3_rest.js where account.owner is a string due to RPC serialization.

Applied to files:

  • src/endpoint/iam/iam_utils.js
📚 Learning: 2025-09-30T08:56:55.478Z
Learnt from: Neon-White
Repo: noobaa/noobaa-core PR: 9229
File: .github/workflows/ibm-nightly-provision-dispatcher.yaml:13-13
Timestamp: 2025-09-30T08:56:55.478Z
Learning: In the noobaa-core repository, PR #9229 (nightly IBM VM provision dispatcher) has a dependency on `.github/ibm-warp-runner-config.yaml` which is provided in PR #9230, requiring PR #9230 to be merged first.

Applied to files:

  • src/endpoint/iam/iam_utils.js
📚 Learning: 2025-08-11T06:12:12.318Z
Learnt from: naveenpaul1
Repo: noobaa/noobaa-core PR: 9182
File: src/upgrade/upgrade_scripts/5.20.0/remove_mongo_pool.js:6-22
Timestamp: 2025-08-11T06:12:12.318Z
Learning: In the noobaa-core upgrade script src/upgrade/upgrade_scripts/5.20.0/remove_mongo_pool.js, bucket migration from the internal mongo pool to a new default pool is planned to be handled in separate future PRs with comprehensive testing, rather than being included directly in the pool removal script.

Applied to files:

  • src/endpoint/iam/iam_utils.js
🧬 Code graph analysis (2)
src/util/account_util.js (3)
src/server/system_services/account_server.js (16)
  • requesting_account (1198-1198)
  • requesting_account (1218-1218)
  • requesting_account (1260-1260)
  • requesting_account (1277-1277)
  • requesting_account (1305-1305)
  • requesting_account (1336-1336)
  • requesting_account (1349-1349)
  • requesting_account (1377-1377)
  • requesting_account (1394-1394)
  • requesting_account (1415-1415)
  • requesting_account (1457-1457)
  • requesting_account (1486-1486)
  • requesting_account (1516-1516)
  • requesting_account (1545-1545)
  • requesting_account (1559-1559)
  • requesting_account (1578-1578)
src/endpoint/s3/s3_bucket_policy_utils.js (1)
  • IAM_DEFAULT_PATH (97-97)
src/endpoint/iam/iam_constants.js (1)
  • IAM_DEFAULT_PATH (69-69)
src/endpoint/s3/s3_rest.js (2)
src/endpoint/iam/iam_utils.js (17)
  • require (6-6)
  • require (7-8)
  • require (10-10)
  • message_with_details (87-87)
  • message_with_details (107-108)
  • message_with_details (281-281)
  • message_with_details (506-507)
  • message_with_details (536-536)
  • message_with_details (563-564)
  • message_with_details (570-571)
  • message_with_details (598-599)
  • message_with_details (619-621)
  • message_with_details (652-652)
  • message_with_details (684-685)
  • message_with_details (704-704)
  • message_with_details (719-719)
  • message_with_details (726-726)
src/endpoint/iam/iam_rest.js (1)
  • method (235-235)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: run-package-lock-validation
  • GitHub Check: Build Noobaa Image
  • GitHub Check: run-jest-unit-tests
🔇 Additional comments (10)
src/util/account_util.js (1)

444-444: LGTM!

The change from requesting_account.path to requesting_account.iam_path || IAM_DEFAULT_PATH correctly aligns with account schemas and provides a proper fallback. Based on the PR objectives, this fixes the field name inconsistency.

src/endpoint/s3/s3_rest.js (8)

17-17: LGTM!

The import of the IAM helper function centralizes error message construction for IAM access denied errors in S3 operations.


335-335: TODO comment aligns with planned refactoring.

The PR objectives mention relocating this function as a future refactor. This is acceptable for now.


345-345: LGTM!

Defaulting resource_arn to '*' for the ListAllMyBuckets operation is correct, as this operation doesn't target a specific bucket resource. This aligns with the PR objective to correct the resource_arn for the ListBuckets operation.


348-352: LGTM!

The added logging and detailed error message when an IAM user has no inline policies configured improves diagnostics and user experience, directly addressing the PR objectives.


367-370: LGTM!

The added logging and use of the detailed error helper for explicit DENY policies improves diagnostics and provides consistent error messaging.


376-378: LGTM!

The added logging and detailed error message when no inline policies match the operation improves diagnostics.


380-384: LGTM!

The helper function cleanly wraps the detailed message construction and error throwing, providing good separation of concerns.


416-416: LGTM!

The early return when bucket is missing prevents constructing an invalid ARN. The caller handles the undefined return by defaulting to '*'.

src/endpoint/iam/iam_utils.js (1)

882-882: LGTM!

The export enables the S3 REST module to use this helper for consistent error messaging. The underscore prefix appropriately indicates this is an internal utility shared between related modules.

@shirady shirady force-pushed the iam-user-policy-error-message branch from 7fe2570 to 6e24c2e Compare November 25, 2025 09:12
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/endpoint/s3/s3_rest.js (1)

380-384: Centralized IAM AccessDenied construction looks good; consider carrying over any extra fields if S3Error defines them

Using _create_detailed_message_for_iam_user_access_in_s3 here and destructuring { code, http_code } from S3Error.AccessDenied keeps the error type and HTTP status consistent while improving the message, which is exactly what we want.

If S3Error.AccessDenied carries any additional fields that other call sites might rely on (e.g., retryable, description, etc.), you might optionally spread them to avoid subtle differences:

-function _throw_iam_access_denied_error_for_s3_operation(requesting_account, method, resource_arn) {
-    const message_with_details = _create_detailed_message_for_iam_user_access_in_s3(requesting_account, method, resource_arn);
-    const { code, http_code } = S3Error.AccessDenied;
-    throw new S3Error({ code, message: message_with_details, http_code});
-}
+function _throw_iam_access_denied_error_for_s3_operation(requesting_account, method, resource_arn) {
+    const message_with_details =
+        _create_detailed_message_for_iam_user_access_in_s3(requesting_account, method, resource_arn);
+    const { code, http_code, ...rest } = S3Error.AccessDenied;
+    throw new S3Error({ code, http_code, ...rest, message: message_with_details });
+}

If code/http_code are the only meaningful fields, the current implementation is fine as‑is.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7fe2570 and 6e24c2e.

📒 Files selected for processing (2)
  • src/endpoint/iam/iam_utils.js (2 hunks)
  • src/endpoint/s3/s3_rest.js (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/endpoint/iam/iam_utils.js
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-11-12T04:55:42.193Z
Learnt from: naveenpaul1
Repo: noobaa/noobaa-core PR: 9277
File: src/endpoint/s3/s3_rest.js:258-261
Timestamp: 2025-11-12T04:55:42.193Z
Learning: In the context of S3 REST requests (src/endpoint/s3/s3_rest.js), the account.owner field from req.object_sdk.requesting_account is already a string (account ID) because it comes from RPC serialization where owner._id.toString() is applied in account_server.js. No additional .toString() or ._id extraction is needed when passing account.owner to IAM utility functions.

Applied to files:

  • src/endpoint/s3/s3_rest.js
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Build Noobaa Image
  • GitHub Check: run-package-lock-validation
  • GitHub Check: run-jest-unit-tests
🔇 Additional comments (2)
src/endpoint/s3/s3_rest.js (2)

345-352: IAM behavior when IAM user has no inline policies now hard‑fails before bucket policy – confirm this is desired

With the new iam_policies.length === 0 branch plus the explicit DENY / “no match” paths all calling _throw_iam_access_denied_error_for_s3_operation, any IAM user in a non‑NC deployment is now denied at the IAM layer unless an inline IAM policy explicitly ALLOWs the requested S3 action. Because authorize_request_iam_policy(req) runs before authorize_request_policy(req), a denial here short‑circuits bucket policy evaluation for IAM users.

If existing workflows ever relied on bucket policies granting access to IAM users that lack IAM user policies, this would be a behavior change; if the intent is “IAM users must always have an IAM user policy in containerized deployments, bucket policies are only an additional filter,” this looks consistent, but it’s worth double‑checking and calling out in docs/release notes.

Also applies to: 367-378


416-423: Guarding _get_arn_from_req_path on missing bucket correctly fixes ListBuckets ARN handling

The if (!req.params.bucket) return; guard, combined with const resource_arn = _get_arn_from_req_path(req) || '*'; in authorize_request_iam_policy, ensures that service‑level operations (like listing all buckets) use '*' instead of generating an invalid arn:aws:s3::: style ARN. This aligns with IAM expectations for ListBuckets/ListAllMyBuckets‑style actions and avoids malformed ARNs.

1. Edit the error message when a user tries to perform an S3 operation to match what it tries to do and have a proper message when executing authorize_request_iam_policy.
2. Add log messages when there is no IAM user policy for better experience.
3. Fix a minor bug when I read the code so I changed the path to be iam_path according to the account schemas in NC and Containerised deployment.
4. Fix the resource_arn in the IAM policy for the list buckets operation.

Signed-off-by: shirady <57721533+shirady@users.noreply.github.com>
@shirady shirady force-pushed the iam-user-policy-error-message branch from 6e24c2e to 15a0277 Compare November 25, 2025 09:22
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/endpoint/iam/iam_utils.js (1)

81-81: Consider more explicit array handling.

The current logic Array.isArray(method) && method.length > 1 ? method[1] : method returns method (the array itself) when it's an array with length ≤ 1, relying on implicit toString() coercion in string interpolation. While this works for single-element arrays, it's not explicit.

Apply this diff for more defensive handling:

-    const full_action_name = Array.isArray(method) && method.length > 1 ? method[1] : method; // special case for get_object_attributes
+    const full_action_name = Array.isArray(method) ? (method[1] ?? method[0]) : method; // special case for get_object_attributes
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6e24c2e and 15a0277.

📒 Files selected for processing (4)
  • src/endpoint/iam/iam_utils.js (2 hunks)
  • src/endpoint/s3/s3_rest.js (5 hunks)
  • src/sdk/accountspace_fs.js (1 hunks)
  • src/util/account_util.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/util/account_util.js
  • src/endpoint/s3/s3_rest.js
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2025-11-12T04:55:42.193Z
Learnt from: naveenpaul1
Repo: noobaa/noobaa-core PR: 9277
File: src/endpoint/s3/s3_rest.js:258-261
Timestamp: 2025-11-12T04:55:42.193Z
Learning: In the context of S3 REST requests (src/endpoint/s3/s3_rest.js), the account.owner field from req.object_sdk.requesting_account is already a string (account ID) because it comes from RPC serialization where owner._id.toString() is applied in account_server.js. No additional .toString() or ._id extraction is needed when passing account.owner to IAM utility functions.

Applied to files:

  • src/sdk/accountspace_fs.js
  • src/endpoint/iam/iam_utils.js
📚 Learning: 2025-11-19T15:03:42.260Z
Learnt from: shirady
Repo: noobaa/noobaa-core PR: 9291
File: src/server/common_services/auth_server.js:548-554
Timestamp: 2025-11-19T15:03:42.260Z
Learning: In src/server/common_services/auth_server.js, account objects are loaded directly from system_store (e.g., system_store.data.get_by_id()), so account.owner is an object ID reference with an ._id property, not a string. This differs from s3_rest.js where account.owner is a string due to RPC serialization.

Applied to files:

  • src/endpoint/iam/iam_utils.js
📚 Learning: 2025-09-30T08:56:55.478Z
Learnt from: Neon-White
Repo: noobaa/noobaa-core PR: 9229
File: .github/workflows/ibm-nightly-provision-dispatcher.yaml:13-13
Timestamp: 2025-09-30T08:56:55.478Z
Learning: In the noobaa-core repository, PR #9229 (nightly IBM VM provision dispatcher) has a dependency on `.github/ibm-warp-runner-config.yaml` which is provided in PR #9230, requiring PR #9230 to be merged first.

Applied to files:

  • src/endpoint/iam/iam_utils.js
📚 Learning: 2025-08-11T06:12:12.318Z
Learnt from: naveenpaul1
Repo: noobaa/noobaa-core PR: 9182
File: src/upgrade/upgrade_scripts/5.20.0/remove_mongo_pool.js:6-22
Timestamp: 2025-08-11T06:12:12.318Z
Learning: In the noobaa-core upgrade script src/upgrade/upgrade_scripts/5.20.0/remove_mongo_pool.js, bucket migration from the internal mongo pool to a new default pool is planned to be handled in separate future PRs with comprehensive testing, rather than being included directly in the pool removal script.

Applied to files:

  • src/endpoint/iam/iam_utils.js
🧬 Code graph analysis (1)
src/endpoint/iam/iam_utils.js (4)
src/endpoint/s3/s3_rest.js (5)
  • method (244-244)
  • method (346-346)
  • method (487-487)
  • resource_arn (345-345)
  • message_with_details (381-381)
src/endpoint/iam/iam_rest.js (1)
  • method (235-235)
src/util/account_util.js (9)
  • owner_account_id (341-341)
  • owner_account_id (717-717)
  • arn_for_requesting_account (443-444)
  • message_with_details (328-328)
  • message_with_details (360-360)
  • message_with_details (388-388)
  • message_with_details (424-424)
  • message_with_details (430-430)
  • message_with_details (436-436)
src/server/system_services/account_server.js (1)
  • owner_account_id (1281-1281)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Build Noobaa Image
  • GitHub Check: run-package-lock-validation
  • GitHub Check: run-jest-unit-tests
🔇 Additional comments (2)
src/endpoint/iam/iam_utils.js (1)

69-94: LGTM - well-structured detailed error message helper.

The function correctly:

  • Computes the requester ARN using iam_path from the user account
  • Handles the special case for s3:ListAllMyBuckets by omitting the resource portion
  • Derives action names from method parameter with special handling for arrays
src/sdk/accountspace_fs.js (1)

677-677: LGTM - correct property name.

This fixes the bug by using requesting_account.iam_path instead of the non-existent requesting_account.path property. The change aligns with the rest of the codebase where iam_path is consistently used (e.g., lines 141, 181, 234, 599, 757, 925).

@shirady shirady merged commit 8204d2a into noobaa:master Nov 25, 2025
18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants