-
Notifications
You must be signed in to change notification settings - Fork 90
IAM | Principal validation and S3 permission updated with ID #9309
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
WalkthroughChanges refactor account/principal identity resolution across S3 bucket policy evaluation layers by normalizing account inputs to arrays in policy utilities, implementing ARN-based principal lookup in bucket server helpers, updating authorization call sites to pass principal arrays, and adjusting S3 REST endpoint routing to support NSFS NC-specific policy checks. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant s3_rest as s3_rest.js
participant auth_server as auth_server.js
participant policy_utils as s3_bucket_policy_utils.js
participant bucket_server as bucket_server.js
Client->>s3_rest: S3 Request
s3_rest->>s3_rest: Extract account_identifier_id from account._id
alt NC Deployment
s3_rest->>auth_server: Check with account_id
auth_server->>policy_utils: has_bucket_policy_permission<br/>[ARN, account_id]
policy_utils->>policy_utils: Normalize to account_arr
policy_utils->>policy_utils: Match principal via includes()
alt ID Check Passed
s3_rest->>s3_rest: Proceed
else ID Check Not Denied
s3_rest->>auth_server: Secondary check with<br/>account_identifier_name
auth_server->>policy_utils: has_bucket_policy_permission<br/>[ARN, account_name]
end
else Containerized Deployment
s3_rest->>auth_server: Check with account_id
auth_server->>policy_utils: has_bucket_policy_permission<br/>[ARN, account_id]
policy_utils->>policy_utils: Normalize to account_arr
alt ARN Available
s3_rest->>auth_server: Verify via ARN check
end
end
auth_server->>bucket_server: get_account_by_principal()<br/>for principal validation
bucket_server->>bucket_server: Check if ARN or ID
bucket_server->>bucket_server: Resolve account object
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes
Possibly related PRs
Suggested reviewers
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
c1a440a to
ca9a081
Compare
Signed-off-by: Naveen Paul <[email protected]>
ca9a081 to
0020baa
Compare
There was a problem hiding this 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
🧹 Nitpick comments (2)
src/server/system_services/bucket_server.js (2)
528-546: Consider removing unnecessaryasynckeyword.This function doesn't contain any
awaitexpressions. Bothsystem_store.data.accounts.find()andsystem_store.get_account_by_email()appear to be synchronous operations based on usage patterns in the codebase. Marking this functionasyncunnecessarily wraps return values in a Promise.If this is intentional for consistency with the caller or future changes, this is fine as-is.
548-567: Function name is misleading - returns boolean, not account.
get_account_by_principalimplies the function returns an account object, but it actually returns a boolean indicating whether the principal exists. Consider renaming to better reflect the return type:
principal_existsis_valid_principalvalidate_principal-async function get_account_by_principal(principal) { +async function principal_exists(principal) {Alternatively, if a getter pattern is preferred, the function could return the account object (or
undefined) and let the caller convert to boolean, which would make the validation callback cleaner and enable future use cases that need the account:async function get_account_by_principal(principal) { const principal_as_string = principal instanceof SensitiveString ? principal.unwrap() : principal; const is_principal_arn = principal_as_string.startsWith('arn:aws:iam::'); if (is_principal_arn) { - const principal_by_arn = await account_exists_by_principal_arn(principal_as_string); - dbg.log3('get_account_by_principal: principal_by_arn', principal_by_arn); - if (principal_by_arn) return true; + return account_exists_by_principal_arn(principal_as_string); } else { - const account = system_store.data.accounts.find(acc => acc._id.toString() === principal_as_string); - const principal_by_id = account !== undefined; - dbg.log3('get_account_by_principal: principal_by_id', principal_by_id); - if (principal_by_id) return true; + return system_store.data.accounts.find(acc => acc._id.toString() === principal_as_string); } - return false; }Then update the callback to convert to boolean:
principal => Boolean(get_account_by_principal(principal))
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
src/endpoint/s3/s3_bucket_policy_utils.js(4 hunks)src/endpoint/s3/s3_rest.js(2 hunks)src/server/common_services/auth_server.js(1 hunks)src/server/system_services/bucket_server.js(2 hunks)src/test/integration_tests/api/iam/test_iam_basic_integration.js(1 hunks)src/test/integration_tests/api/s3/test_s3_bucket_policy.js(10 hunks)src/util/account_util.js(0 hunks)
💤 Files with no reviewable changes (1)
- src/util/account_util.js
🧰 Additional context used
📓 Path-based instructions (1)
src/test/**/*.*
⚙️ CodeRabbit configuration file
src/test/**/*.*: Ensure that the PR includes tests for the changes.
Files:
src/test/integration_tests/api/iam/test_iam_basic_integration.jssrc/test/integration_tests/api/s3/test_s3_bucket_policy.js
🧠 Learnings (6)
📓 Common learnings
Learnt from: naveenpaul1
Repo: noobaa/noobaa-core PR: 9277
File: src/endpoint/s3/s3_bucket_policy_utils.js:357-368
Timestamp: 2025-11-18T07:00:17.653Z
Learning: In NooBaa codebase, account.name is always a SensitiveString instance, so calling account.name.unwrap() is safe without defensive type checks in functions like get_bucket_policy_principal_arn in src/endpoint/s3/s3_bucket_policy_utils.js.
📚 Learning: 2025-11-13T07:56:23.620Z
Learnt from: shirady
Repo: noobaa/noobaa-core PR: 9281
File: src/server/system_services/account_server.js:1053-1058
Timestamp: 2025-11-13T07:56:23.620Z
Learning: In noobaa-core, account_server.js is only used in containerized deployments, not in NSFS/NC deployments. NSFS/NC deployments have separate account management code in src/manage_nsfs/ directory. Therefore, account_server.js only processes accounts from account_schema.js where owner is an objectid reference, never from nsfs_account_schema.js where owner is a string.
Applied to files:
src/endpoint/s3/s3_rest.jssrc/test/integration_tests/api/s3/test_s3_bucket_policy.js
📚 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.jssrc/test/integration_tests/api/s3/test_s3_bucket_policy.jssrc/endpoint/s3/s3_bucket_policy_utils.js
📚 Learning: 2025-11-18T07:00:17.653Z
Learnt from: naveenpaul1
Repo: noobaa/noobaa-core PR: 9277
File: src/endpoint/s3/s3_bucket_policy_utils.js:357-368
Timestamp: 2025-11-18T07:00:17.653Z
Learning: In NooBaa codebase, account.name is always a SensitiveString instance, so calling account.name.unwrap() is safe without defensive type checks in functions like get_bucket_policy_principal_arn in src/endpoint/s3/s3_bucket_policy_utils.js.
Applied to files:
src/endpoint/s3/s3_rest.jssrc/server/common_services/auth_server.jssrc/test/integration_tests/api/s3/test_s3_bucket_policy.jssrc/endpoint/s3/s3_bucket_policy_utils.jssrc/server/system_services/bucket_server.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/s3/s3_rest.jssrc/test/integration_tests/api/s3/test_s3_bucket_policy.jssrc/server/system_services/bucket_server.js
📚 Learning: 2025-08-08T13:08:38.361Z
Learnt from: naveenpaul1
Repo: noobaa/noobaa-core PR: 9182
File: src/server/system_services/bucket_server.js:1324-1327
Timestamp: 2025-08-08T13:08:38.361Z
Learning: In src/server/system_services/bucket_server.js, the update_all_buckets_default_pool(req) handler expects req.rpc_params.pool_name to be a plain string (not a SensitiveString wrapper), so calling .unwrap() is not needed there.
Applied to files:
src/server/system_services/bucket_server.js
🧬 Code graph analysis (6)
src/endpoint/s3/s3_rest.js (1)
src/endpoint/s3/s3_bucket_policy_utils.js (1)
account(303-303)
src/server/common_services/auth_server.js (3)
src/endpoint/s3/s3_bucket_policy_utils.js (1)
account(303-303)src/endpoint/s3/s3_rest.js (2)
account(252-252)account(343-343)src/server/system_services/bucket_server.js (1)
account(561-561)
src/test/integration_tests/api/iam/test_iam_basic_integration.js (1)
src/test/system_tests/test_utils.js (1)
is_nc_coretest(48-48)
src/test/integration_tests/api/s3/test_s3_bucket_policy.js (2)
src/test/system_tests/test_utils.js (1)
is_nc_coretest(48-48)src/endpoint/s3/s3_bucket_policy_utils.js (2)
account(303-303)_(4-4)
src/endpoint/s3/s3_bucket_policy_utils.js (1)
src/endpoint/iam/iam_utils.js (1)
statement_principal(723-723)
src/server/system_services/bucket_server.js (6)
src/server/system_services/account_server.js (11)
SensitiveString(15-15)dbg(12-12)account(69-69)account(117-117)account(135-137)account(159-159)account(207-207)account(330-330)account(693-693)account(957-957)system_store(17-17)src/api/account_api.js (1)
SensitiveString(4-4)src/cmd/manage_nsfs.js (1)
SensitiveString(25-25)src/api/common_api.js (1)
SensitiveString(4-4)src/cmd/nsfs.js (1)
SensitiveString(38-38)src/sdk/bucketspace_nb.js (1)
dbg(7-7)
⏰ 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-jest-unit-tests
- GitHub Check: run-package-lock-validation
🔇 Additional comments (14)
src/test/integration_tests/api/iam/test_iam_basic_integration.js (1)
76-78: LGTM!The conditional cleanup correctly ensures
folder_deleteonly runs whenis_nc_coretestis true, matching the condition under whichconfig_rootis set in thebeforehook. This prevents attempting to delete an undefined path in containerized deployments.src/server/common_services/auth_server.js (1)
558-566: LGTM!The change to pass
[arn, account._id.toString()]as an array aligns with the updatedhas_bucket_policy_permissionsignature that now accepts an array of account identifiers. This enables policy principal matching against both the ARN and the account ID, supporting the PR's objective of ID-based validation.src/endpoint/s3/s3_rest.js (3)
255-257: LGTM!The simplification to always source
account_identifier_idfromaccount._idis correct. Both NC and containerized deployments now validate bucket policies against the account ID, aligning with the PR's objective of ID-based principal validation.
313-320: LGTM!The name-based permission check is now correctly scoped to NSFS NC deployments only, and restricted to root accounts (
account.owner === undefined). This maintains backward compatibility for NC while preventing IAM users from matching by name.
322-330: LGTM!The ARN-based permission check is now explicitly limited to containerized deployments (
!is_nc_deployment). This correctly separates the authorization paths: NC uses ID + name, while containerized uses ID + ARN.src/endpoint/s3/s3_bucket_policy_utils.js (3)
153-168: LGTM!The JSDoc update and
account_arrnormalization correctly document the function's ability to accept either a string or an array of account identifiers. The normalization patternArray.isArray(account) ? account : [account]ensures consistent array handling downstream.
204-222: LGTM!The refactored
_is_principal_fitfunction correctly usesaccount_arr.includes(principal.unwrap())for matching, allowing a principal to match any identifier in the array (ID, ARN, or name). The wildcard checkprincipal.unwrap() === '*'is evaluated first, maintaining correct anonymous access handling.
239-251: LGTM!The signature changes to
is_statement_fit_of_method_arrayand_is_statements_fitcorrectly propagateaccount_arrthrough the call chain, ensuring consistent array-based principal matching throughout the policy evaluation flow.src/test/integration_tests/api/s3/test_s3_bucket_policy.js (5)
68-70: LGTM!The addition of
user_a_account_idanduser_b_account_idvariables provides a consistent way to reference account IDs across tests, abstracting the difference between NC (_id) and containerized (id) response formats.
127-134: LGTM!The account ID extraction correctly handles the different response formats: NC deployments use
_idwhile containerized deployments useid. The added console logging will aid debugging.
156-157: LGTM!The principal construction correctly differentiates between NC (account name) and containerized (ARN from account ID) deployments, aligning with how each deployment validates principals.
315-362: LGTM!The test cases for principal by account ID correctly use
user_a_account_idin both the policy Sid and Principal AWS fields, properly testing ID-based authorization for both NC and containerized deployments.
482-484: Tests enabled for both deployment types.The removal of
this.skip()guards for NC-only tests (// if (!is_nc_coretest) this.skip();) enables these account-ID-based policy tests to run on both NC and containerized deployments. This aligns with the PR goal of enabling ID-based validation across deployment types.Also applies to: 517-519, 583-585
src/server/system_services/bucket_server.js (1)
572-573: No issues found - async callback handling is correct.The callback is properly awaited by
validate_s3_policyat line 303 ofs3_bucket_policy_utils.js:const account = await get_account_handler(principal);. The Promise returned byget_account_by_principalis correctly handled.
| } | ||
| // Losing this value in-between, assigning it again | ||
| a_principal = is_nc_coretest ? user_a : s3_bucket_policy_utils.create_arn_for_root(account_info_a._id.toString()); | ||
| a_principal = is_nc_coretest ? user_a : s3_bucket_policy_utils.create_arn_for_root(user_b_account_id.toString()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug: a_principal is constructed using user_b_account_id instead of user_a.
For containerized deployments, a_principal is being set with user_b_account_id, which is incorrect. This will cause the deny_account_by_name_all_s3_actions_statement to target user_b instead of user_a in containerized tests.
Apply this diff to fix the bug:
- a_principal = is_nc_coretest ? user_a : s3_bucket_policy_utils.create_arn_for_root(user_b_account_id.toString());
+ a_principal = is_nc_coretest ? user_a : s3_bucket_policy_utils.create_arn_for_root(user_a_account_id.toString());📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| a_principal = is_nc_coretest ? user_a : s3_bucket_policy_utils.create_arn_for_root(user_b_account_id.toString()); | |
| a_principal = is_nc_coretest ? user_a : s3_bucket_policy_utils.create_arn_for_root(user_a_account_id.toString()); |
🤖 Prompt for AI Agents
In src/test/integration_tests/api/s3/test_s3_bucket_policy.js around line 397,
the code constructs a_principal using user_b_account_id when it should use
user_a's account id for containerized deployments; update the ternary fallback
to use s3_bucket_policy_utils.create_arn_for_root(user_a_account_id.toString())
instead of user_b_account_id so the
deny_account_by_name_all_s3_actions_statement targets user_a as intended.
Describe the Problem
Explain the Changes
Principal validation
S3 Rest authorize request policy
Testing Instructions:
user2_idcould be ID of different account or IAM user ID of IAM user(not belongs to account1)3. Verify the
user2havebucket1access.Summary by CodeRabbit
Bug Fixes
Tests
✏️ Tip: You can customize this high-level summary in your review settings.