-
Notifications
You must be signed in to change notification settings - Fork 90
IAM | CreateUser, UpdateUser - UserName, NewUserName Check #9323
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
Signed-off-by: shirady <[email protected]>
Signed-off-by: shirady <[email protected]>
WalkthroughThe PR introduces username as a public parameter to the create_user API and propagates it through account creation and update flows. Username collision detection is now case-insensitive, checking against lowercase usernames. Integration tests verify the new validation behavior across creation and update operations. Changes
Sequence DiagramsequenceDiagram
participant Client
participant API as API Layer
participant Server as Account Server
participant Util as Account Util
participant Store as System Store
Client->>API: create_user(username, email, ...)
API->>Server: req.rpc_params.username
Server->>Util: _check_username_already_exists(action, email, username)
Util->>Util: _check_if_account_exists_by_email(email)
Note over Util: Case-insensitive<br/>email search
Util->>Store: Search accounts by email
alt Account exists
Store-->>Util: account
Util->>Util: username.toLowerCase()
Note over Util: Compare against<br/>existing username
Util-->>Server: EntityAlreadyExists error
Server-->>API: Error response
API-->>Client: 409 Conflict
else Account not found
Store-->>Util: null
Util-->>Server: Check passed
Server->>Server: Create/update user with username
Server->>Store: Persist account
Store-->>Server: Success
Server-->>API: Success response
API-->>Client: 200 OK
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 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 |
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: 3
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
src/api/account_api.js(1 hunks)src/sdk/accountspace_fs.js(1 hunks)src/sdk/accountspace_nb.js(1 hunks)src/server/system_services/account_server.js(2 hunks)src/test/integration_tests/api/iam/test_iam_advanced_integration.js(1 hunks)src/test/utils/index/index.js(1 hunks)src/test/utils/index/nc_index.js(1 hunks)src/util/account_util.js(1 hunks)
🧰 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/utils/index/nc_index.jssrc/test/utils/index/index.jssrc/test/integration_tests/api/iam/test_iam_advanced_integration.js
🧠 Learnings (3)
📚 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/util/account_util.jssrc/sdk/accountspace_fs.jssrc/server/system_services/account_server.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/sdk/accountspace_fs.jssrc/server/system_services/account_server.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/server/system_services/account_server.js
🧬 Code graph analysis (5)
src/sdk/accountspace_nb.js (1)
src/server/system_services/account_server.js (3)
params(328-328)params(691-691)params(955-955)
src/util/account_util.js (2)
src/sdk/accountspace_nb.js (1)
system_store(5-5)src/test/utils/coretest/coretest.js (1)
system_store(49-49)
src/test/utils/index/nc_index.js (2)
src/sdk/accountspace_nb.js (1)
require(6-6)src/util/account_util.js (4)
require(8-8)require(14-14)require(15-15)require(16-17)
src/sdk/accountspace_fs.js (4)
src/server/system_services/account_server.js (2)
owner_account_id(1184-1184)owner_account_id(1292-1292)src/util/account_util.js (2)
owner_account_id(341-341)owner_account_id(723-723)src/endpoint/s3/s3_rest.js (1)
owner_account_id(337-337)src/endpoint/iam/iam_utils.js (2)
owner_account_id(78-78)owner_account_id(851-851)
src/server/system_services/account_server.js (1)
src/endpoint/iam/iam_constants.js (1)
IAM_ACTIONS(5-23)
⏰ 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: run-jest-unit-tests
- GitHub Check: Build Noobaa Image
🔇 Additional comments (9)
src/test/utils/index/index.js (1)
113-114: LGTM!The new IAM advanced integration test is correctly added alongside the existing basic integration test.
src/sdk/accountspace_nb.js (1)
41-53: LGTM!The
usernamefield is correctly added to the request object, aligning with the updated API schema.src/api/account_api.js (1)
679-681: LGTM!The
usernameparameter is correctly added to thecreate_userAPI schema as an optional string field.src/test/utils/index/nc_index.js (1)
28-29: LGTM!The NC test bootstrap correctly includes the new advanced IAM integration test alongside the basic test.
src/test/integration_tests/api/iam/test_iam_advanced_integration.js (2)
30-74: Good test setup with comprehensive case-insensitive username testing.The test suite correctly validates case-insensitive username uniqueness for both CreateUser and UpdateUser operations. The setup/teardown logic properly handles test isolation.
209-215: LGTM!Simple and effective helper function for validating HTTP 200 responses.
src/util/account_util.js (1)
355-369: Case-insensitive username check implementation looks correct.The new
_check_if_account_exists_by_emailhelper properly performs case-insensitive email comparison by usingtoLowerCase()on both sides of the comparison. The null-safety withaccounts || []is a good defensive pattern.src/sdk/accountspace_fs.js (1)
796-807: Verify case-insensitive handling in account name lookups.The code converts username to lowercase before checking existence via
is_account_exists_by_name(), but the implementation details of that method and how account names are stored cannot be confirmed. Ensure this method performs case-insensitive comparison internally or that account names are consistently stored in lowercase. Additionally, verify that other account lookups (e.g.,_check_if_account_config_file_exists) use the same case-insensitive approach to avoid inconsistency.src/server/system_services/account_server.js (1)
42-48: IAM create_user path now cleanly uses explicit username and case‑insensitive uniquenessUsing
req.rpc_params.usernamefor both the root‑account IAM check and ARN construction, while delegating the case‑insensitive uniqueness check toaccount_util._check_username_already_exists, looks correct and aligns with the new API contract. No additional changes needed here.
src/test/integration_tests/api/iam/test_iam_advanced_integration.js
Outdated
Show resolved
Hide resolved
src/test/integration_tests/api/iam/test_iam_advanced_integration.js
Outdated
Show resolved
Hide resolved
Signed-off-by: shirady <[email protected]>
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
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
src/server/system_services/account_server.js(2 hunks)src/test/integration_tests/api/iam/test_iam_advanced_integration.js(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- src/test/integration_tests/api/iam/test_iam_advanced_integration.js
🧰 Additional context used
🧠 Learnings (4)
📚 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/server/system_services/account_server.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/server/system_services/account_server.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/server/system_services/account_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/account_server.js
🧬 Code graph analysis (1)
src/server/system_services/account_server.js (1)
src/endpoint/iam/iam_constants.js (1)
IAM_ACTIONS(5-23)
⏰ 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 (1)
src/server/system_services/account_server.js (1)
1237-1244: Past issue resolved: guarded computation looks correct.The code now correctly guards
email_new_usernamecomputation and the uniqueness check behind theis_username_updatecondition. This addresses the concern from the previous review about callingget_account_name_from_usernamewithundefinedwhen no username change is requested.
Signed-off-by: shirady <[email protected]>
Signed-off-by: shirady <[email protected]>
bbd49fe to
e837a5d
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: 0
🧹 Nitpick comments (1)
src/test/integration_tests/api/iam/test_iam_advanced_integration.js (1)
145-165: Consider adding a test for updating to a different user's name.The current UpdateUser tests validate case variants of the same user. For more comprehensive coverage, consider adding a test that creates two users and attempts to update one user's name to match the other user's name.
Example test structure:
mocha.it('update a user to another existing user name should fail', async function() { // Create a second user const secondUser = 'Alice'; await iam_account.send(new CreateUserCommand({ UserName: secondUser })); try { // Try to update first user to second user's name const command = new UpdateUserCommand({ UserName: username, NewUserName: secondUser, }); await iam_account.send(command); assert.fail('update user to existing username - should throw an error'); } catch (err) { assert.equal(err.Error.Code, IamError.EntityAlreadyExists.code); } finally { await iam_account.send(new DeleteUserCommand({ UserName: secondUser })); } });
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/test/integration_tests/api/iam/test_iam_advanced_integration.js(1 hunks)
🧰 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_advanced_integration.js
🧬 Code graph analysis (1)
src/test/integration_tests/api/iam/test_iam_advanced_integration.js (4)
src/sdk/accountspace_nb.js (1)
require(6-6)src/test/utils/index/nc_index.js (1)
coretest(5-5)src/test/utils/index/index.js (1)
coretest(5-5)src/test/system_tests/test_utils.js (2)
is_nc_coretest(48-48)TMP_PATH(23-23)
⏰ 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 (7)
src/test/integration_tests/api/iam/test_iam_advanced_integration.js (7)
1-28: LGTM! Test setup and imports are well-structured.The imports appropriately pull in necessary utilities, AWS SDK commands, and test infrastructure. The conditional setup for NC vs non-NC environments follows established patterns.
33-68: LGTM! Test account and IAM client setup is thorough.The setup correctly handles both NC and non-NC test environments, properly unwraps sensitive credentials, and establishes the IAM client for subsequent tests.
81-143: Good test coverage for case-insensitive username collision in CreateUser.The tests properly verify that usernames are unique in a case-insensitive manner by checking exact match, lowercase, and uppercase variants. The setup/teardown hooks ensure proper test isolation.
145-205: LGTM! UpdateUser tests correctly validate case-insensitive username handling.The tests verify that:
- Updating to the same username succeeds (line 166-174)
- Updating to case variants of the existing username fails with
EntityAlreadyExists(lines 176-204)The previous review comments about incorrect assertion messages have been addressed.
210-216: LGTM! Helper function is well-documented.The
_check_status_code_okhelper provides a clean abstraction for response validation with appropriate JSDoc type annotation.
110-113: Verify error property access pattern matches SDK response structure.The error access pattern
err.Error.Codeassumes a specific error structure. If the SDK throws a differently-structured error, accessingerr.Error.Codecould throw a TypeError. Verify this pattern is consistent with actual error responses from the IAM SDK in this codebase.
70-74: Missingawaiton async cleanup call.
fs_utils.folder_deleteappears to be an async function based on standard Node.js patterns. Withoutawait, the cleanup may not complete before the test process exits, potentially leaving orphaned test directories.mocha.after(async () => { if (is_nc_coretest) { - fs_utils.folder_delete(`${config_root}`); + await fs_utils.folder_delete(`${config_root}`); } });
Signed-off-by: shirady <[email protected]>
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 (1)
src/test/integration_tests/api/iam/test_iam_basic_integration.js (1)
930-955: Avoid coupling advanced suite to prior describe’s setup
iam_accountis used here but only initialized in thebeforehook of the earlier"IAM basic integration tests - happy path"suite. This makes the advanced tests depend on that suite running first; usingdescribe.onlyon this block (or running suites in isolation) will leaveiam_accountundefined.Consider factoring the IAM client setup into a shared helper or a top-level
beforeso each top-level suite can initializeiam_accountindependently.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/test/integration_tests/api/iam/test_iam_basic_integration.js(1 hunks)
🧰 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.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: run-package-lock-validation
- GitHub Check: Build Noobaa Image
- GitHub Check: run-jest-unit-tests
🔇 Additional comments (1)
src/test/integration_tests/api/iam/test_iam_basic_integration.js (1)
936-999: CreateUser case-insensitive duplicate tests look solidThe three CreateUser tests correctly cover:
- duplicate creation with the exact same username
- duplicate creation with lowercase
- duplicate creation with uppercase
all asserting
IamError.EntityAlreadyExists. This aligns well with the PR goal of enforcing case-insensitive uniqueness on user creation and reuses the existing error-checking pattern in this file.
| mocha.describe('IAM UpdateUser API', async function() { | ||
| mocha.before(async () => { | ||
| // create a user | ||
| const input = { | ||
| UserName: username | ||
| }; | ||
| const command = new CreateUserCommand(input); | ||
| const response = await iam_account.send(command); | ||
| _check_status_code_ok(response); | ||
| }); | ||
|
|
||
| mocha.after(async () => { | ||
| // delete a user | ||
| const input = { | ||
| UserName: username | ||
| }; | ||
| const command = new DeleteUserCommand(input); | ||
| const response = await iam_account.send(command); | ||
| _check_status_code_ok(response); | ||
| }); | ||
|
|
||
| mocha.it('update a user with same username', async function() { | ||
| const input = { | ||
| UserName: username, | ||
| NewUserName: username, | ||
| }; | ||
| const command = new UpdateUserCommand(input); | ||
| const response = await iam_account.send(command); | ||
| _check_status_code_ok(response); | ||
| }); | ||
|
|
||
| mocha.it('update a user with new username that already exists (lower case) should fail', async function() { | ||
| try { | ||
| const input = { | ||
| UserName: username, | ||
| NewUserName: username_lowercase, | ||
| }; | ||
| const command = new UpdateUserCommand(input); | ||
| await iam_account.send(command); | ||
| assert.fail('update user with existing username (lower case) - should throw an error'); | ||
| } catch (err) { | ||
| const err_code = err.Error.Code; | ||
| assert.equal(err_code, IamError.EntityAlreadyExists.code); | ||
| } | ||
| }); | ||
|
|
||
| mocha.it('update a user with new username that already exists (upper case) should fail', async function() { | ||
| try { | ||
| const input = { | ||
| UserName: username, | ||
| NewUserName: username_uppercase, | ||
| }; | ||
| const command = new UpdateUserCommand(input); | ||
| await iam_account.send(command); | ||
| assert.fail('update user with existing username (upper case) - should throw an error'); | ||
| } catch (err) { | ||
| const err_code = err.Error.Code; | ||
| assert.equal(err_code, IamError.EntityAlreadyExists.code); | ||
| } | ||
| }); | ||
| }); |
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.
UpdateUser “duplicate username” tests don’t actually create a conflicting user
In the IAM UpdateUser API block you only ever create a single user (username). The two negative tests:
update a user with new username that already exists (lower case) should failupdate a user with new username that already exists (upper case) should fail
attempt to set NewUserName to username_lowercase / username_uppercase for that same user, but there is no other user with those names. As written, these tests will only fail if the backend incorrectly treats a rename of a user to a different casing of its own name as a conflict, and they do not validate “username already exists” behavior between distinct users.
To actually exercise case-insensitive uniqueness on update, you should create a second, conflicting user and then try to rename the first user to that name (in different casings). For example:
mocha.describe('IAM UpdateUser API', async function() {
+ const conflicting_username = `${username}_conflict`;
+ const conflicting_username_lowercase = conflicting_username.toLowerCase();
+ const conflicting_username_uppercase = conflicting_username.toUpperCase();
+
mocha.before(async () => {
- // create a user
- const input = {
- UserName: username
- };
- const command = new CreateUserCommand(input);
- const response = await iam_account.send(command);
- _check_status_code_ok(response);
+ // create two users so we can test collisions on update
+ for (const name of [username, conflicting_username]) {
+ const input = { UserName: name };
+ const command = new CreateUserCommand(input);
+ const response = await iam_account.send(command);
+ _check_status_code_ok(response);
+ }
});
mocha.after(async () => {
- // delete a user
- const input = {
- UserName: username
- };
- const command = new DeleteUserCommand(input);
- const response = await iam_account.send(command);
- _check_status_code_ok(response);
+ // delete both users
+ for (const name of [username, conflicting_username]) {
+ const input = { UserName: name };
+ const command = new DeleteUserCommand(input);
+ const response = await iam_account.send(command);
+ _check_status_code_ok(response);
+ }
});
@@
mocha.it('update a user with new username that already exists (lower case) should fail', async function() {
try {
const input = {
UserName: username,
- NewUserName: username_lowercase,
+ NewUserName: conflicting_username_lowercase,
};
@@
mocha.it('update a user with new username that already exists (upper case) should fail', async function() {
try {
const input = {
UserName: username,
- NewUserName: username_uppercase,
+ NewUserName: conflicting_username_uppercase,
};This way you test that updating one user to a name already used by another user (in any casing) correctly yields IamError.EntityAlreadyExists, while still allowing no-op updates with the same username.
🤖 Prompt for AI Agents
In src/test/integration_tests/api/iam/test_iam_basic_integration.js around lines
1000-1060, the two negative UpdateUser tests only create a single user
(username) so they never exercise a conflict with a distinct existing user;
create a second user with the conflicting name (username_lowercase /
username_uppercase) before attempting the UpdateUser call (either in the
mocha.before for this describe block or at the start of each negative test),
then attempt to rename the original user to that second user’s name (different
casing) and assert EntityAlreadyExists, and finally ensure the second user is
deleted in the mocha.after cleanup (or removed at test end) so both created
users are cleaned up.
Describe the Problem
Users name should be unique within the account, they aren’t distinguished by case.
Explain the Changes
usernamein thecreate_userAPI.test_iam_basic_integration).Issues:
none
Testing Instructions:
Automatic test
Containerized
docker run -p 5432:5432 -e POSTGRESQL_ADMIN_PASSWORD=noobaa quay.io/sclorg/postgresql-15-c9sNOOBAA_LOG_LEVEL=all ./node_modules/.bin/mocha src/test/integration_tests/api/iam/test_iam_advanced_integration.jsNC:
sudo NC_CORETEST=true ./node_modules/.bin/mocha src/test/integration_tests/api/iam/test_iam_advanced_integration.jssudo npx jest test_accountspace_fs.test.js(unit tests)Summary by CodeRabbit
New Features
Tests
✏️ Tip: You can customize this high-level summary in your review settings.