Skip to content

Conversation

@naveenpaul1
Copy link
Contributor

@naveenpaul1 naveenpaul1 commented Oct 13, 2025

Describe the Problem

This PR contains implementation for the following IAM account APIs

IAM GetUser: UserName
IAM CreateUser: UserName
IAM UpdateUser: NewPath, NewUserName, UserName
IAM DeleteUser: UserName
IAM ListUsers: PathPrefix

NOTE: DB changes added in this PR could change after desig discusion.

Explain the Changes

  1. Extent AccountSDK
  2. Implement AccountSpace
  3. Update DB for for IAM

Issues: Fixed #xxx / Gap #xxx

Testing Instructions:

  1. Crate IAM client
  2. Execute IAM account CLI commands.
iam create-user --user-name iam-first1 --path /division_abc/subdivision_xyz/ 
iam update-user --user-name iam-first1 --new-user-name Bob --new-path /division_abc/newpath/
iam list-users
iam get-user --user-name Bob 
iam delete-user --user-name Bob

FYI : Noobaa operator changes are missing, Needs to create IAM service manually for testing

kind: Service
apiVersion: v1
metadata:
  name: iam
  namespace: noobaa
  labels:
    app: noobaa
spec:
  type: LoadBalancer
  ports:
    - name: iam-https
      protocol: TCP
      port: 443
      targetPort: 7444
  selector:
    noobaa-s3: noobaa
  • Doc added/updated
  • Tests added

Summary by CodeRabbit

  • New Features

    • IAM user management: create, retrieve, update, delete, and list IAM users.
    • New NB account SDK and per-request account context for IAM-aware operations.
    • Account records now include IAM metadata (ARN, path), owner and tagging.
    • Added IAM role type ("iam_user") and expanded system/api support for "iam".
  • Refactor

    • Centralized account creation, deletion, and credential generation into a shared utility.

@coderabbitai
Copy link

coderabbitai bot commented Oct 13, 2025

Walkthrough

Adds IAM/account-management features and refactors: new NBAccountSDK and AccountSpaceNB, per-request req.account_sdk initialization and EndpointRequest typedef update, centralized account_util for account creation/deletion/keys, account-server delegation, IAM-related schema and constant additions, and new IAM-aware account SDK wiring.

Changes

Cohort / File(s) Summary
Endpoint init & SDK wiring
src/endpoint/endpoint.js, src/sdk/nb_account_sdk.js
Adds NBAccountSDK; initializes and attaches req.account_sdk = new NBAccountSDK({ rpc_client, internal_rpc_client }) in request init. EndpointRequest typedef now includes account_sdk.
IAM Account Space
src/sdk/accountspace_nb.js
New AccountSpaceNB class implementing IAM-aware account operations: create_user, get_user, update_user, delete_user, list_users; access-key methods stubbed (throw NotImplemented).
Centralized account utilities
src/util/account_util.js
New account_util with create_account, delete_account, generate_account_keys, IAM/ARN helpers, validation/authz checks, auditing hooks, and multiple exported helper functions.
Account server refactor
src/server/system_services/account_server.js
Delegates create/generate-keys/delete flows to account_util (removed previous inline logic and related dependencies).
Schema updates
src/server/system_services/schemas/account_schema.js, src/server/system_services/schemas/role_schema.js, src/server/system_services/schemas/system_schema.js
account_schema adds owner, tagging, iam_arn, iam_path and removes next_password_change; role_schema adds iam_user to role enum; system_schema adds 'iam' to system_address.service and system_address.api.
IAM constants
src/endpoint/iam/iam_constants.js
Adds and exports IAM_SPLIT_CHARACTERS with value ':'.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Endpoint
    participant Req as Request
    participant NBSDK as NBAccountSDK
    participant AccountSpace
    participant AccountUtil
    participant Store as SystemStore

    Client->>Endpoint: HTTP API call
    Endpoint->>Req: create_init_request_sdk(req)
    Req->>NBSDK: new NBAccountSDK(rpc_client, internal_rpc_client)
    NBSDK-->>Req: attach req.account_sdk

    Client->>Endpoint: call account operation (e.g., create_user)
    Endpoint->>AccountSpace: AccountSpaceNB.create_user(params, req.account_sdk)
    AccountSpace->>AccountUtil: validate & request create_account(...)
    AccountUtil->>Store: read/write account records / keys
    Store-->>AccountUtil: operation result
    AccountUtil-->>AccountSpace: created account metadata
    AccountSpace-->>Endpoint: formatted user response
    Endpoint-->>Client: API response
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Suggested labels

size/L

Suggested reviewers

  • dannyzaken
  • jackyalbo
  • liranmauda

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 32.14% 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 PR title "IAM_NOOBAA | IAM Create account" is specifically related to the changeset and clearly refers to the CreateUser functionality, which is indeed implemented in this PR. However, the pull request's primary objective is to implement a complete IAM account management system encompassing multiple APIs: CreateUser, GetUser, UpdateUser, DeleteUser, and ListUsers. The title captures only one aspect of the broader implementation and does not reflect the full scope of the IAM account management features being added, including schema changes, AccountSpaceNB infrastructure, and account utilities.
✨ 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.

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: 9

🧹 Nitpick comments (12)
config.js (2)

1025-1025: Default‑enabling IAM on 7444 may unintentionally expose a new port; mirror STS gating

Previously IAM was disabled by default (-1). Consider disabling unless explicitly enabled, and mirror the STS NC_NSFS_NO_DB_ENV guard.

Apply:

-config.ENDPOINT_SSL_IAM_PORT = Number(process.env.ENDPOINT_SSL_IAM_PORT) || 7444;
+config.ENDPOINT_SSL_IAM_PORT = Number(process.env.ENDPOINT_SSL_IAM_PORT)
+    || (process.env.NC_NSFS_NO_DB_ENV === 'true' ? -1 : 7444);

1029-1031: ALLOW_HTTP_METRICS/ALLOW_HTTPS_METRICS are not enforced

These flags are defined but not used when starting metrics servers. Gate the ports in endpoint.js.

Add in endpoint.js (inside main):

-const http_metrics_port = options.http_metrics_port || config.EP_METRICS_SERVER_PORT;
-const https_metrics_port = options.https_metrics_port || config.EP_METRICS_SERVER_SSL_PORT;
+const http_metrics_port = config.ALLOW_HTTP_METRICS
+  ? (options.http_metrics_port || config.EP_METRICS_SERVER_PORT)
+  : 0;
+const https_metrics_port = config.ALLOW_HTTPS_METRICS
+  ? (options.https_metrics_port || config.EP_METRICS_SERVER_SSL_PORT)
+  : 0;
src/endpoint/iam/iam_constants.js (1)

61-61: Naming nit: prefer singular or a clearer intent

IAM_SPLIT_CHARACTERS holds a single ':'; consider renaming to IAM_SPLIT_CHARACTER or IAM_ARN_DELIM for clarity.

Also applies to: 74-74

src/endpoint/endpoint.js (1)

97-105: Add account_sdk to EndpointRequest typedef

JSDoc does not include account_sdk.

Apply:

  * @typedef {import('http').IncomingMessage & {
   *  object_sdk?: ObjectSDK;
   *  sts_sdk?: StsSDK;
+  *  account_sdk?: AccountSDK;
   *  virtual_hosts?: readonly string[];
   *  bucket_logger?: PersistentLogger;
   *  notification_logger?: PersistentLogger;
   * }} EndpointRequest
src/sdk/accountspace_nb.js (5)

56-56: Use IAM_SPLIT_CHARACTERS for composing pseudo-email

Avoid inlining ':' to keep consistency with split logic.

-        const account_name = new SensitiveString(`${params.username}:${requesting_account.name.unwrap()}`);
+        const account_name = new SensitiveString(
+            `${params.username}${IAM_SPLIT_CHARACTERS}${requesting_account.name.unwrap()}`
+        );

103-112: Use actual account timestamps; remove placeholders

Replace placeholders with real values if available; otherwise omit.

-            // TODO: Dates missing : GAP
-            create_date: new Date(),
-            password_last_used: new Date(),
+            create_date: requested_account.last_update ? new Date(requested_account.last_update) : undefined,
+            password_last_used: undefined, // until tracked

Please confirm last_update exists on accounts; if the schema stores a create_time, use that instead.


167-172: Minor: normalize details object to use path key

Align with _throw_access_denied_error:

-        account_util._check_if_requesting_account_is_root_account(action, requesting_account, { username: params.username });
+        account_util._check_if_requesting_account_is_root_account(action, requesting_account, { username: params.username, path: params.iam_path });

193-211: List users: prefer owner/is_iam filter over string parsing; fix date field

  • Filtering via name split is brittle. Use is_iam and owner === requesting_account._id.
  • Use a real timestamp from the account doc.

Apply:

-        const requesting_account_iam_users = _.filter(system_store.data.accounts, function(acc) {
-            if (!acc.name.unwrap().includes(IAM_SPLIT_CHARACTERS)) {
-                return false;
-            }
-            return acc.name.unwrap().split(IAM_SPLIT_CHARACTERS)[1] === root_name;
-        });
+        const root_id = String(requesting_account._id);
+        const requesting_account_iam_users = _.filter(system_store.data.accounts, acc =>
+            acc.is_iam === true && acc.owner === root_id
+        );
@@
-                create_date: iam_user.creation_date,
-                password_last_used: Date.now(), // GAP
+                create_date: iam_user.last_update ? new Date(iam_user.last_update) : undefined,
+                password_last_used: undefined,

Please confirm is_iam and owner are present on IAM users; otherwise we can fallback to name-split as a secondary condition.


220-249: Stubs: consider returning NotImplemented consistently with code/message

Current pattern is fine. Optionally, include the action name in the message to aid clients, e.g., message: 'CreateAccessKey NotImplemented'.

src/util/account_util.js (3)

59-70: Avoid hardcoded default_resource from callers

create_account already throws if a provided default_resource doesn't exist. Recommend callers omit default_resource unless validated. No code change here; documenting the expectation.


286-303: validate_assume_role_policy: minor robustness

If policy.statement or nested arrays contain non-SensitiveString principals, principal.unwrap() throws. Optionally normalize with principal instanceof SensitiveString ? principal.unwrap() : principal.


414-436: _throw_access_denied_error: expects details.path; align call sites

Callers pass iam_path. Either accept both keys or standardize on path. You fixed call sites above; alternatively make this tolerant:

-            user_message = create_arn_for_user(account_id_for_arn, details.username, details.path);
+            user_message = create_arn_for_user(
+                account_id_for_arn,
+                details.username,
+                details.path ?? details.iam_path
+            );
📜 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 69d598a and 3836ab1.

📒 Files selected for processing (9)
  • config.js (1 hunks)
  • src/endpoint/endpoint.js (3 hunks)
  • src/endpoint/iam/iam_constants.js (2 hunks)
  • src/sdk/accountspace_nb.js (1 hunks)
  • src/server/system_services/account_server.js (4 hunks)
  • src/server/system_services/schemas/account_schema.js (1 hunks)
  • src/server/system_services/schemas/role_schema.js (1 hunks)
  • src/server/system_services/schemas/system_schema.js (2 hunks)
  • src/util/account_util.js (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
src/sdk/accountspace_nb.js (2)
src/util/account_util.js (21)
  • _ (4-4)
  • require (8-8)
  • require (14-14)
  • require (17-17)
  • require (18-18)
  • SensitiveString (9-9)
  • dbg (7-7)
  • system_store (12-12)
  • IamError (15-15)
  • IamError (312-312)
  • IamError (345-345)
  • IamError (373-373)
  • IamError (410-410)
  • IamError (434-434)
  • req (525-525)
  • account (31-43)
  • account (198-198)
  • account (291-291)
  • account (308-308)
  • account (341-341)
  • account (468-468)
src/endpoint/iam/iam_constants.js (3)
  • IAM_ACTIONS (5-16)
  • IAM_DEFAULT_PATH (47-47)
  • IAM_SPLIT_CHARACTERS (61-61)
src/endpoint/endpoint.js (1)
src/sdk/account_sdk.js (1)
  • BucketSpaceNB (9-9)
src/util/account_util.js (2)
src/server/system_services/account_server.js (31)
  • _ (5-5)
  • require (13-13)
  • require (21-21)
  • require (22-22)
  • require (23-23)
  • Dispatcher (14-14)
  • dbg (12-12)
  • SensitiveString (15-15)
  • cloud_utils (16-16)
  • system_store (17-17)
  • req (118-118)
  • req (382-382)
  • account (53-53)
  • account (101-101)
  • account (120-122)
  • account (144-144)
  • account (192-192)
  • account (315-315)
  • account (678-678)
  • account (942-942)
  • roles (448-448)
  • account_to_delete (402-402)
  • roles_to_delete (414-414)
  • access_keys (156-156)
  • system (197-197)
  • system (1016-1016)
  • email (99-99)
  • params (314-314)
  • params (677-677)
  • params (941-941)
  • status (892-892)
src/endpoint/iam/iam_constants.js (2)
  • IAM_ACTIONS (5-16)
  • ACCESS_KEY_STATUS_ENUM (34-37)
src/server/system_services/account_server.js (1)
src/sdk/accountspace_nb.js (1)
  • account_util (6-6)
⏰ 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 (7)
src/server/system_services/schemas/role_schema.js (1)

28-29: Ensure auth/role checks recognize 'iam_user'

Adding 'iam_user' is fine. Verify any role-based checks, UI filters, and seeding/migrations include it (e.g., roles_by_system usage, admin/operator checks).

src/server/system_services/schemas/system_schema.js (1)

90-92: LGTM: schema now includes IAM service/api

Enums updated to include 'iam'. Make sure address discovery/registration emits entries for IAM accordingly.

Also applies to: 101-103

src/endpoint/endpoint.js (2)

461-465: Per-request NBAccountSDK wiring: LGTM; ensure consumers use req.account_sdk

Looks good. Verify iam_rest and any other handlers read req.account_sdk and not instantiate their own.


75-96: AccountSDK constructor signature matches injected spaces
AccountSDK’s constructor accepts {rpc_client, internal_rpc_client, bucketspace, accountspace}, matching NBAccountSDK’s super call.

src/server/system_services/account_server.js (2)

135-136: Delegating key generation: LGTM

Centralizing in account_util is cleaner.


38-45: Safe to return only { token, access_keys }: all existing callers only access those two fields.

src/util/account_util.js (1)

142-147: Return field names: create_date vs creation_date

Upstream consumers used creation_date. This function returns create_date. Keep it as create_date but ensure consumers reference it correctly (see fixes in AccountSpaceNB).

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: 6

♻️ Duplicate comments (7)
src/sdk/accountspace_nb.js (6)

52-54: Pass details.path (not details.iam_path) to access-denied builder

Wrong key breaks error messages/ARNs.

-        account_util._check_if_requesting_account_is_root_account(action, requesting_account,
-                { username: params.username, iam_path: params.iam_path });
+        account_util._check_if_requesting_account_is_root_account(
+            action,
+            requesting_account,
+            { username: params.username, path: params.iam_path }
+        );

58-70: Persist iam_path on created IAM user and remove hardcoded default_resource

Avoids stale path in later reads and brittle resource dependency.

             rpc_params: {
                 name: account_name,
                 email: account_name,
                 has_login: false,
                 s3_access: true,
                 allow_bucket_creation: true,
                 owner: requesting_account._id.toString(),
                 is_iam: true,
                 iam_arn: iam_arn,
+                iam_path: params.iam_path || IAM_DEFAULT_PATH,
                 role: 'iam_user',
-                // TODO: default_resource remove
-                default_resource: 'noobaa-default-backing-store',
             },

80-86: Fix returned iam_path and create_date

Use the created user’s path and the create_account reply field.

-            iam_path: requesting_account.iam_path || IAM_DEFAULT_PATH,
+            iam_path: params.iam_path || IAM_DEFAULT_PATH,
             username: params.username,
             user_id: iam_account.id,
             arn: iam_arn,
-            create_date: iam_account.creation_date,
+            create_date: iam_account.create_date,

95-96: Pass details.path (not details.iam_path) to access-denied builder

Same issue as in create_user.

-        account_util._check_if_requesting_account_is_root_account(action, requesting_account,
-                { username: params.username, iam_path: params.iam_path });
+        account_util._check_if_requesting_account_is_root_account(
+            action,
+            requesting_account,
+            { username: params.username, path: params.iam_path }
+        );

120-121: Pass details.path (not details.iam_path) to access-denied builder

Same fix as above.

-        account_util._check_if_requesting_account_is_root_account(action, requesting_account,
-                { username: params.username, iam_path: params.iam_path });
+        account_util._check_if_requesting_account_is_root_account(
+            action,
+            requesting_account,
+            { username: params.username, path: params.iam_path }
+        );

167-168: Pass details.path (not details.iam_path) to access-denied builder

Align with access-denied message builder expectations.

-        account_util._check_if_requesting_account_is_root_account(action, requesting_account, { username: params.username });
+        account_util._check_if_requesting_account_is_root_account(
+            action,
+            requesting_account,
+            { username: params.username, path: params.iam_path }
+        );
src/util/account_util.js (1)

114-118: Persist iam_path for IAM users

Required for get/list/update correctness.

     if (req.rpc_params.is_iam) {
         account.owner = req.rpc_params.owner;
         account.is_iam = req.rpc_params.is_iam;
         account.iam_arn = req.rpc_params.iam_arn;
+        if (req.rpc_params.iam_path) {
+            account.iam_path = req.rpc_params.iam_path;
+        }
     }
📜 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 3836ab1 and b6bee2c.

📒 Files selected for processing (2)
  • src/sdk/accountspace_nb.js (1 hunks)
  • src/util/account_util.js (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
src/sdk/accountspace_nb.js (3)
src/util/account_util.js (20)
  • _ (4-4)
  • require (8-8)
  • require (14-14)
  • require (17-17)
  • require (18-18)
  • SensitiveString (9-9)
  • system_store (12-12)
  • IamError (15-15)
  • IamError (312-312)
  • IamError (345-345)
  • IamError (373-373)
  • IamError (410-410)
  • IamError (434-434)
  • req (525-525)
  • account (31-43)
  • account (198-198)
  • account (291-291)
  • account (308-308)
  • account (341-341)
  • account (468-468)
src/server/system_services/account_server.js (22)
  • _ (5-5)
  • require (13-13)
  • require (21-21)
  • require (22-22)
  • require (23-23)
  • SensitiveString (15-15)
  • account_util (25-25)
  • account_util (40-40)
  • system_store (17-17)
  • params (314-314)
  • params (677-677)
  • params (941-941)
  • req (118-118)
  • req (382-382)
  • account (53-53)
  • account (101-101)
  • account (120-122)
  • account (144-144)
  • account (192-192)
  • account (315-315)
  • account (678-678)
  • account (942-942)
src/endpoint/iam/iam_constants.js (3)
  • IAM_ACTIONS (5-16)
  • IAM_DEFAULT_PATH (47-47)
  • IAM_SPLIT_CHARACTERS (61-61)
src/util/account_util.js (1)
src/endpoint/iam/iam_constants.js (2)
  • IAM_ACTIONS (5-16)
  • ACCESS_KEY_STATUS_ENUM (34-37)
⏰ 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 (1)
src/util/account_util.js (1)

544-555: Export set looks good

Public helpers align with SDK usage.

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

♻️ Duplicate comments (3)
src/util/account_util.js (3)

18-19: Use IAM_DEFAULT_PATH and iam_path when constructing ARNs; add the missing import

Import IAM_DEFAULT_PATH and default to it when path is absent. Also prefer iam_path over requesting_account.path to avoid undefined in ARNs. This fixes incorrect/noisy error messages.

-const { IAM_ACTIONS, ACCESS_KEY_STATUS_ENUM } = require('../endpoint/iam/iam_constants');
+const { IAM_ACTIONS, ACCESS_KEY_STATUS_ENUM, IAM_DEFAULT_PATH } = require('../endpoint/iam/iam_constants');
 function _throw_access_denied_error(action, requesting_account, details, entity) {
   const full_action_name = get_action_message_title(action);
   const account_id_for_arn = _get_account_owner_id_for_arn(requesting_account);
-  const arn_for_requesting_account = create_arn_for_user(account_id_for_arn,
-      requesting_account.name.unwrap(), requesting_account.path);
+  const arn_for_requesting_account = create_arn_for_user(
+      account_id_for_arn,
+      requesting_account.name.unwrap(),
+      requesting_account.iam_path || IAM_DEFAULT_PATH
+  );
   const basic_message = `User: ${arn_for_requesting_account} is not authorized to perform:` +
   `${full_action_name} on resource: `;
   let message_with_details;
   if (entity === 'USER') {
       let user_message;
       if (action === IAM_ACTIONS.LIST_ACCESS_KEYS) {
           user_message = `user ${details.username}`;
       } else {
-          user_message = create_arn_for_user(account_id_for_arn, details.username, details.path);
+          user_message = create_arn_for_user(
+              account_id_for_arn,
+              details.username instanceof SensitiveString ? details.username.unwrap() : details.username,
+              (details.path && details.path.unwrap ? details.path.unwrap() : details.path) || IAM_DEFAULT_PATH
+          );
       }
       message_with_details = basic_message +
       `${user_message} because no identity-based policy allows the ${full_action_name} action`;
   } else {
       message_with_details = basic_message + `access key ${details.access_key}`;
   }
   const { code, http_code, type } = IamError.AccessDeniedException;
   throw new IamError({ code, message: message_with_details, http_code, type });
 }

Also applies to: 414-433


114-118: Persist iam_path for IAM users during create_account

Currently iam_path is never stored; later reads expect requested_account.iam_path.

   if (req.rpc_params.is_iam) {
       account.owner = req.rpc_params.owner;
       account.is_iam = req.rpc_params.is_iam;
       account.iam_arn = req.rpc_params.iam_arn;
+      if (req.rpc_params.iam_path) {
+          account.iam_path = req.rpc_params.iam_path;
+      }
   }

448-456: Avoid undefined account id in ARN owner resolution

Provide a fallback when requested_account is not supplied.

 function _get_account_owner_id_for_arn(requesting_account, requested_account) {
   if (!requesting_account.iam_operate_on_root_account) {
       if (requesting_account.owner !== undefined) {
           return requesting_account.owner;
       }
       return requesting_account._id;
   }
-  return requested_account?._id;
+  return requested_account ? requested_account._id : requesting_account._id;
 }
🧹 Nitpick comments (2)
src/util/account_util.js (2)

11-12: Normalize require paths (double slash)

Minor cleanup; prevents path inconsistencies.

-const auth_server = require('..//server/common_services/auth_server');
-const system_store = require('..//server/system_services/system_store').get_instance();
+const auth_server = require('../server/common_services/auth_server');
+const system_store = require('../server/system_services/system_store').get_instance();

98-106: PII in logs (emails)

Emails are PII; prefer masking or structured logging with redaction.

  • Mask emails (e.g., a***@example.com) or omit them at info/alert levels.
  • Centralize masking in Dispatcher or a log helper to avoid leaks.

Also applies to: 168-176, 179-187, 232-239

📜 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 6ed9db3 and 7137dbf.

📒 Files selected for processing (1)
  • src/util/account_util.js (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/util/account_util.js (2)
src/sdk/accountspace_nb.js (5)
  • _ (4-4)
  • require (12-12)
  • SensitiveString (5-5)
  • system_store (9-9)
  • IamError (11-11)
src/endpoint/iam/iam_constants.js (2)
  • IAM_ACTIONS (5-16)
  • ACCESS_KEY_STATUS_ENUM (34-37)
⏰ 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

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

♻️ Duplicate comments (9)
src/server/system_services/account_server.js (1)

405-405: Verify delete_user handles full account cleanup for non-IAM accounts.

The function name delete_user suggests IAM-specific logic. Ensure it performs complete cleanup (buckets, policies, access keys, roles, etc.) for both IAM and classic accounts, not just role removal.

Based on past review feedback.

Run this script to verify the delete_user implementation:

#!/bin/bash
# Verify delete_user handles full account cleanup
rg -n -A 20 'function delete_user|exports\.delete_user' src/util/account_util.js
src/sdk/accountspace_nb.js (8)

128-128: Only check for duplicate username if renaming.

The duplicate check runs even when params.new_username is undefined, and doesn't verify the new name differs from the current one.

Apply this diff:

         let new_user_name = requested_account.name.unwrap().split(IAM_SPLIT_CHARACTERS)[0];
-        account_util._check_username_already_exists(action, { username: params.new_username }, requesting_account);
         const root_account = _.find(system_store.data.accounts, account => account.name.unwrap() === requesting_account.name.unwrap());
         account_util._check_if_requested_account_is_root_account_or_IAM_user(action, requesting_account, requested_account);
         account_util._check_if_requested_is_owned_by_root_account(action, root_account, requested_account);
         if (params.new_iam_path !== undefined) new_iam_path = params.new_iam_path;
-        if (params.new_username !== undefined) new_user_name = params.new_username;
+        if (params.new_username !== undefined && params.new_username !== new_user_name) {
+            new_user_name = params.new_username;
+            account_util._check_username_already_exists(action, { username: params.new_username }, requesting_account);
+        }

Based on past review feedback.


153-159: Return updated values, not stale or undefined ones.

The response uses stale or potentially undefined values. After the fix to line 127, new_user_name will be correct. However, ensure new_iam_path is defined (it might be undefined if requested_account.iam_path is undefined).

Apply this diff to ensure defaults:

         return {
-            // TODO: IAM path needs to be saved
             iam_path: new_iam_path || IAM_DEFAULT_PATH,
             username: new_user_name,
             user_id: requested_account._id.toString(),
             arn: iam_arn
         };

Based on past review feedback.


70-71: Remove hardcoded default_resource.

Hardcoding 'noobaa-default-backing-store' will fail if that resource doesn't exist. Let account_util.create_account resolve the default resource automatically.

Apply this diff:

                 iam_path: params.iam_path,
                 role: 'iam_user',
-
-                // TODO: default_resource remove
-                default_resource: 'noobaa-default-backing-store',
             },

Based on past review feedback.


81-88: Return the created user's iam_path, not the requesting account's.

Line 83 returns requesting_account.iam_path, but the response should reflect the created user's IAM path from params.iam_path.

Apply this diff:

         return {
-            // TODO : PATH Missing
-            iam_path: requesting_account.iam_path || IAM_DEFAULT_PATH,
+            iam_path: params.iam_path || IAM_DEFAULT_PATH,
             username: params.username,
             user_id: iam_account.id,
             arn: iam_arn,
             create_date: iam_account.create_date,
         };

Based on past review feedback.


111-113: Use persisted creation timestamp and drop placeholder password_last_used.

  • Line 111: requested_account.last_update is the last modification time, not creation time. Accounts should track creation timestamp separately.
  • Line 113: Returning new Date() for password_last_used is misleading—it returns "now" instead of the actual last password use time.

Apply this diff:

             arn: requested_account.iam_arn,
-            create_date: new Date(requested_account.last_update),
-            // TODO: Dates missing : GAP
-            password_last_used: new Date(),
+            create_date: requested_account.create_date || new Date(requested_account.last_update),
+            password_last_used: undefined, // Unknown; avoid misleading timestamps
         };

Based on past review feedback.


127-127: Extract username without root suffix.

requested_account.name.unwrap() includes the root account suffix (e.g., username:root). Extract just the username portion.

Apply this diff:

         const requested_account = system_store.get_account_by_email(account_name);
         let new_iam_path = requested_account.iam_path;
-        let new_user_name = requested_account.name.unwrap();
+        let new_user_name = requested_account.name.unwrap().split(IAM_SPLIT_CHARACTERS)[0];
         account_util._check_username_already_exists(action, { username: params.new_username }, requesting_account);

Based on past review feedback.


136-140: Persist iam_path in updates.

The updates object doesn't include iam_path, so path changes won't be saved to the database.

Apply this diff:

         const updates = {
             name: new_account_name,
             email: new_account_name,
             iam_arn: iam_arn,
+            iam_path: new_iam_path,
         };

Based on past review feedback.


188-218: Implement PathPrefix filtering and fix timestamp issues.

  • Missing PathPrefix filtering: The method ignores params.path_prefix (or params.PathPrefix), so it returns all users regardless of their IAM path.
  • Line 209: Uses last_update instead of creation timestamp.
  • Line 211: Returns Date.now() instead of actual password last-used time.

Apply this diff:

     async list_users(params, account_sdk) {
         const action = IAM_ACTIONS.LIST_USERS;
-        //const requesting_account = account_sdk.requesting_account;
         const requesting_account = system_store.get_account_by_email(account_sdk.requesting_account.email);
         account_util._check_if_requesting_account_is_root_account(action, requesting_account, { });
         const is_truncated = false; // GAP - no pagination at this point
+        const path_prefix = params?.path_prefix || params?.PathPrefix;
 
         const root_name = requesting_account.name.unwrap();
-        // CORE CHANGES PENDING - START
         const requesting_account_iam_users = _.filter(system_store.data.accounts, function(acc) {
             if (!acc.name.unwrap().includes(IAM_SPLIT_CHARACTERS)) {
                 return false;
             }
             return acc.name.unwrap().split(IAM_SPLIT_CHARACTERS)[1] === root_name;
         });
         let members = _.map(requesting_account_iam_users, function(iam_user) {
+            if (path_prefix && !(iam_user.iam_path || IAM_DEFAULT_PATH).startsWith(path_prefix)) {
+                return undefined;
+            }
             const member = {
                 user_id: iam_user._id.toString(),
                 iam_path: iam_user.iam_path || IAM_DEFAULT_PATH,
                 username: iam_user.name.unwrap().split(IAM_SPLIT_CHARACTERS)[0],
                 arn: iam_user.iam_arn,
-                create_date: new Date(iam_user.last_update),
-                // TODO: Miising password_last_used
-                password_last_used: Date.now(), // GAP
+                create_date: iam_user.create_date || new Date(iam_user.last_update),
+                password_last_used: undefined, // Unknown; avoid misleading timestamps
             };
             return member;
         });
-        // CORE CHANGES PENDING - END
+        members = members.filter(Boolean);
         members = members.sort((a, b) => a.username.localeCompare(b.username));
         return { members, is_truncated };
     }

Based on past review feedback.

🧹 Nitpick comments (2)
src/server/system_services/schemas/account_schema.js (2)

31-31: Consider renaming owner to owner_account for clarity.

The field name owner is ambiguous. Using owner_account makes it explicit that this references the owning account record.

Based on past review feedback.


36-36: Add validation pattern for iam_arn.

IAM ARNs follow AWS format (arn:partition:iam:region:account-id:resource-type/resource-id). Adding a pattern constraint prevents invalid ARN values.

Consider adding:

-iam_arn: { type: 'string' },
+iam_arn: { 
+    type: 'string',
+    pattern: '^arn:[^:]*:iam:[^:]*:[^:]*:(user|role)/.+$'
+},

Based on past review feedback.

📜 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 7137dbf and 5fef7ca.

📒 Files selected for processing (4)
  • src/sdk/accountspace_nb.js (1 hunks)
  • src/server/system_services/account_server.js (4 hunks)
  • src/server/system_services/schemas/account_schema.js (1 hunks)
  • src/util/account_util.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/util/account_util.js
🧰 Additional context used
🧬 Code graph analysis (2)
src/sdk/accountspace_nb.js (2)
src/util/account_util.js (21)
  • _ (4-4)
  • require (8-8)
  • require (14-14)
  • require (17-17)
  • require (18-18)
  • SensitiveString (9-9)
  • dbg (7-7)
  • system_store (12-12)
  • IamError (15-15)
  • IamError (312-312)
  • IamError (345-345)
  • IamError (373-373)
  • IamError (410-410)
  • IamError (434-434)
  • req (522-522)
  • account (31-43)
  • account (199-199)
  • account (291-291)
  • account (308-308)
  • account (341-341)
  • account (465-465)
src/endpoint/iam/iam_constants.js (3)
  • IAM_ACTIONS (5-16)
  • IAM_DEFAULT_PATH (47-47)
  • IAM_SPLIT_CHARACTERS (61-61)
src/server/system_services/account_server.js (2)
src/sdk/accountspace_nb.js (2)
  • account_util (6-6)
  • require (12-12)
src/util/account_util.js (5)
  • require (8-8)
  • require (14-14)
  • require (17-17)
  • require (18-18)
  • req (522-522)
⏰ 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 (3)
src/server/system_services/account_server.js (2)

39-46: LGTM: Delegation to account_util centralizes account creation logic.

The refactor to delegate account creation to account_util improves code organization and reusability.


136-136: LGTM: Delegation to account_util for key generation.

Consistent with the overall refactoring pattern to centralize account operations.

src/sdk/accountspace_nb.js (1)

163-186: LGTM: IAM user deletion flow is correct.

The method properly validates ownership and delegates to account_util.delete_user.

@nimrod-becker
Copy link
Contributor

@dannyzaken @jackyalbo @shirady PTAL

Copy link
Contributor

@shirady shirady left a comment

Choose a reason for hiding this comment

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

Adding initial comments (high level)

next_password_change: { date: true }, // DEPRECATED

// TODO: Check NSFS behaviour for account and iam user
owner: { objectid: true },
Copy link
Contributor

@shirady shirady Oct 21, 2025

Choose a reason for hiding this comment

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

  • I looked at the NC schema (link): We had the 'owner' and 'creator' as we thought that in the future users can be created not only by the account root user. Anyway, in NC the IAM account implemented could by created only by account root user.
  • Could you give example of this field when it is account root user and when it is IAM user?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The IAM user could not create another IAM user. In that case owner always will be the root user.

Copy link
Contributor

Choose a reason for hiding this comment

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

I just want to make sure - about this field (owner):
admin - undefined
IAM user - admin _id value in accounts table
Did I understand you correctly?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes, Only IAM user will have value in owner field.

role: {
type: 'string',
enum: ['admin', 'user', 'operator']
enum: ['admin', 'user', 'operator', 'iam_user']
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do we need 'iam_user' and also 'user'?
Do you know in which cases do we use 'user'?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

no, default value for current account is admin

Copy link
Contributor

Choose a reason for hiding this comment

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

In general I don't mind adding this enum, but I'm not sure exactly where we use this role_schema.

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

♻️ Duplicate comments (8)
src/server/system_services/account_server.js (1)

405-405: Verify delete_user handles all account types, not just IAM users.

The function name delete_user suggests IAM-specific deletion, but it's being called from delete_account for all account types. Ensure account_util.delete_user properly handles:

  • Cleanup of roles (already implemented based on code context)
  • Cleanup of buckets owned by classic (non-IAM) accounts
  • Removal of bucket policies, user policies, and access keys
  • Deletion of any other account-scoped resources

If delete_user only handles IAM users, consider either (A) extending it to handle all account types or (B) renaming it to reflect its actual scope and creating a separate function for classic account deletion.

Run the following script to inspect the delete_user implementation:

#!/bin/bash
# Inspect account_util.delete_user to verify it handles all account types
rg -n -A 20 "function delete_user|exports.delete_user|delete_user.*=" src/util/account_util.js
src/sdk/accountspace_nb.js (7)

126-127: Extract username without root suffix for correct default.

Line 127 assigns the full account name (including :root_account_name suffix) to new_user_name. This should extract only the username portion using split(IAM_SPLIT_CHARACTERS)[0].

Apply this diff:

         const requested_account = system_store.get_account_by_email(account_name);
         let new_iam_path = requested_account.iam_path;
-        let new_user_name = requested_account.name.unwrap();
+        let new_user_name = requested_account.name.unwrap().split(IAM_SPLIT_CHARACTERS)[0];

81-88: Return the created user's iam_path, not the requester's.

Line 83 returns requesting_account.iam_path (the root account's path), but should return the newly created IAM user's path from params.iam_path.

Apply this diff:

         return {
-            // TODO : PATH Missing
-            iam_path: requesting_account.iam_path || IAM_DEFAULT_PATH,
+            iam_path: params.iam_path || IAM_DEFAULT_PATH,
             username: params.username,
             user_id: iam_account.id,
             arn: iam_arn,
             create_date: iam_account.create_date,
         };

136-140: Persist iam_path in the updates object.

The iam_path field is not included in the updates object, so path changes won't be saved. Add iam_path: new_iam_path to persist the updated path.

Apply this diff:

         const updates = {
             name: new_account_name,
             email: new_account_name,
             iam_arn: iam_arn,
+            iam_path: new_iam_path,
         };

97-98: Use consistent parameter name 'path' in access-denied details.

Line 98 passes { username: params.username, iam_path: params.iam_path }, but _check_if_requesting_account_is_root_account and the error builder expect path, not iam_path. This breaks error messages.

Apply this diff:

         account_util._check_if_requesting_account_is_root_account(action, requesting_account,
-                { username: params.username, iam_path: params.iam_path });
+                { username: params.username, path: params.iam_path });

70-71: Remove hardcoded default_resource and let account creation resolve it.

The hardcoded 'noobaa-default-backing-store' may not exist in all deployments and will cause creation failures. Remove this line and allow account_util.create_account to resolve the default resource automatically.

Apply this diff:

                 iam_path: params.iam_path,
                 role: 'iam_user',
-
-                // TODO: default_resource remove
-                default_resource: 'noobaa-default-backing-store',
             },

188-218: Implement PathPrefix filtering and fix password_last_used.

Two issues in list_users:

  1. Missing PathPrefix filter: The method doesn't filter by params.path_prefix. Users should only be returned if their iam_path starts with the specified prefix.

  2. Placeholder timestamp: Line 211 uses Date.now() for password_last_used, which is misleading.

Apply this diff:

         const is_truncated = false; // GAP - no pagination at this point
+        const path_prefix = params.path_prefix || params.PathPrefix;
 
         const root_name = requesting_account.name.unwrap();
         // CORE CHANGES PENDING - START
         const requesting_account_iam_users = _.filter(system_store.data.accounts, function(acc) {
             if (!acc.name.unwrap().includes(IAM_SPLIT_CHARACTERS)) {
                 return false;
             }
+            if (path_prefix && !(acc.iam_path || IAM_DEFAULT_PATH).startsWith(path_prefix)) {
+                return false;
+            }
             return acc.name.unwrap().split(IAM_SPLIT_CHARACTERS)[1] === root_name;
         });
         let members = _.map(requesting_account_iam_users, function(iam_user) {
             const member = {
                 user_id: iam_user._id.toString(),
                 iam_path: iam_user.iam_path || IAM_DEFAULT_PATH,
                 username: iam_user.name.unwrap().split(IAM_SPLIT_CHARACTERS)[0],
                 arn: iam_user.iam_arn,
                 create_date: new Date(iam_user.last_update),
-                // TODO: Miising password_last_used
-                password_last_used: Date.now(), // GAP
+                // Password last used is not tracked; omit to avoid misleading data
+                password_last_used: undefined,
             };
             return member;
         });

111-113: Use persisted timestamps instead of placeholder dates.

Lines 111-113 use new Date() for both create_date and password_last_used, which returns "now" and is misleading. Use persisted timestamps or omit unknown fields.

Apply this diff:

             arn: requested_account.iam_arn,
-            create_date: new Date(requested_account.last_update),
-            // TODO: Dates missing : GAP
-            password_last_used: new Date(),
+            create_date: new Date(requested_account.last_update || requested_account._id.getTimestamp()),
+            // Password last used is not tracked; omit or return undefined
+            password_last_used: requested_account.password_last_used || undefined,
         };
🧹 Nitpick comments (2)
src/server/system_services/schemas/account_schema.js (1)

30-33: Consider adding validation and uniqueness constraints for IAM fields.

The schema additions look good and address previous review concerns by using objectid for owner. However, consider these optional enhancements:

  • Add a pattern validation for iam_arn to enforce AWS ARN format (e.g., ^arn:[^:]*:iam:.*$)
  • Consider a unique index on iam_arn at the model/database layer to prevent duplicates

These additions would strengthen data integrity for IAM resources.

src/endpoint/endpoint.js (1)

74-95: LGTM: NBAccountSDK properly wires account and bucket spaces.

The class correctly composes BucketSpaceNB and AccountSpaceNB into the AccountSDK. The comment on lines 75-76 about "simple flow was not implemented" is unclear—consider clarifying or removing it before merge.

📜 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 5fef7ca and ddbfc2a.

📒 Files selected for processing (8)
  • src/endpoint/endpoint.js (3 hunks)
  • src/endpoint/iam/iam_constants.js (2 hunks)
  • src/sdk/accountspace_nb.js (1 hunks)
  • src/server/system_services/account_server.js (4 hunks)
  • src/server/system_services/schemas/account_schema.js (1 hunks)
  • src/server/system_services/schemas/role_schema.js (1 hunks)
  • src/server/system_services/schemas/system_schema.js (2 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/iam/iam_constants.js
🧰 Additional context used
🧬 Code graph analysis (2)
src/server/system_services/account_server.js (1)
src/sdk/accountspace_nb.js (1)
  • account_util (6-6)
src/sdk/accountspace_nb.js (4)
src/server/system_services/account_server.js (21)
  • _ (5-5)
  • require (13-13)
  • require (21-21)
  • require (22-22)
  • require (23-23)
  • account_util (25-25)
  • account_util (41-41)
  • system_store (17-17)
  • params (315-315)
  • params (678-678)
  • params (942-942)
  • req (119-119)
  • req (383-383)
  • account (54-54)
  • account (102-102)
  • account (121-123)
  • account (145-145)
  • account (193-193)
  • account (316-316)
  • account (679-679)
  • account (943-943)
src/util/account_util.js (19)
  • _ (4-4)
  • require (8-8)
  • require (14-14)
  • require (17-17)
  • require (18-18)
  • system_store (12-12)
  • IamError (15-15)
  • IamError (312-312)
  • IamError (345-345)
  • IamError (373-373)
  • IamError (410-410)
  • IamError (434-434)
  • req (522-522)
  • account (31-43)
  • account (199-199)
  • account (291-291)
  • account (308-308)
  • account (341-341)
  • account (465-465)
src/endpoint/endpoint.js (7)
  • require (39-39)
  • require (40-40)
  • require (42-42)
  • require (43-43)
  • require (45-45)
  • system_store (36-36)
  • AccountSpaceNB (49-49)
src/endpoint/iam/iam_constants.js (3)
  • IAM_ACTIONS (5-16)
  • IAM_DEFAULT_PATH (47-47)
  • IAM_SPLIT_CHARACTERS (61-61)
⏰ 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 (5)
src/server/system_services/schemas/role_schema.js (1)

28-28: LGTM: IAM user role type added.

The addition of the 'iam_user' role type appropriately extends the role enum to support IAM users alongside existing admin, user, and operator roles.

src/server/system_services/schemas/system_schema.js (1)

90-91: LGTM: IAM service and API enums updated.

The additions properly register IAM as both a service type and API type in system addresses, enabling IAM endpoint configuration.

Also applies to: 102-102

src/endpoint/endpoint.js (1)

461-465: LGTM: Account SDK properly initialized per request.

The initialization of req.account_sdk follows the same pattern as req.sts_sdk and req.object_sdk, making IAM account operations available to endpoint handlers.

src/server/system_services/account_server.js (1)

39-41: LGTM: Account creation properly delegated to account_util.

The refactoring cleanly centralizes account creation logic, maintaining the same interface while improving maintainability.

src/sdk/accountspace_nb.js (1)

224-253: LGTM: Access key methods appropriately stubbed.

The access key methods are clearly marked as not implemented and throw appropriate errors. This is acceptable for an initial IAM implementation focusing on user management APIs.

Copy link
Contributor

@shirady shirady left a comment

Choose a reason for hiding this comment

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

I added comments.
You don't think we need to fix everything in this PR (those that are related to implementation details can be handled in next PRs).

NEW_USERNAME: 'NewUserName',
});

const IAM_SPLIT_CHARACTERS = ':';
Copy link
Contributor

Choose a reason for hiding this comment

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

Would be better to add a comment about this variable use.
And if you move the functions related to the account name of IAM user, it can also move to there.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

we need to move the IAM specific method like create user (name:account_name), and get only user name methods also need to move to IAM util

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: 2

♻️ Duplicate comments (7)
src/util/account_util.js (3)

462-470: Provide fallback when requested_account is not supplied.

Line 469 can return undefined when requesting_account.iam_operate_on_root_account is true but requested_account is not provided, leading to "undefined" appearing in ARNs.

Apply this diff to add a fallback:

 function _get_account_owner_id_for_arn(requesting_account, requested_account) {
     if (!requesting_account.iam_operate_on_root_account) {
         if (requesting_account.owner !== undefined) {
             return requesting_account.owner;
         }
         return requesting_account._id;
     }
-    return requested_account?._id;
+    return requested_account?._id || requesting_account._id;
 }

507-511: Fix resource existence check (comparing object to 0 is always false).

Line 509 compares req.system.pools_by_name to 0, which will always be false for an object. This masks "no resources" conditions and can allow account creation to proceed incorrectly.

Apply this diff to fix the check:

-            if (req.system.pools_by_name === 0) {
+            if (!req.system.pools_by_name || Object.keys(req.system.pools_by_name).length === 0) {
                 throw new RpcError('No resources in the system - Can\'t create accounts');
             }

428-435: Use iam_path instead of undefined path property.

Line 432 references requesting_account.path which is not a defined property on account objects. This should be requesting_account.iam_path to correctly construct the ARN for error messages.

Apply this diff:

     const account_id_for_arn = _get_account_owner_id_for_arn(requesting_account);
     const arn_for_requesting_account = create_arn_for_user(account_id_for_arn,
-        requesting_account.name.unwrap(), requesting_account.path || IAM_DEFAULT_PATH);
+        requesting_account.name.unwrap(), requesting_account.iam_path || IAM_DEFAULT_PATH);
src/sdk/accountspace_nb.js (4)

183-214: Implement PathPrefix filtering in list_users.

The list_users method ignores the PathPrefix parameter, which is a required filter for AWS IAM compatibility. Additionally, lines 205 and 207 return incorrect placeholder dates.

Apply this diff to add PathPrefix filtering and fix dates:

     async list_users(params, account_sdk) {
         const action = IAM_ACTIONS.LIST_USERS;
-        //const requesting_account = account_sdk.requesting_account;
         const requesting_account = system_store.get_account_by_email(account_sdk.requesting_account.email);
         account_util._check_if_requesting_account_is_root_account(action, requesting_account, { });
         const is_truncated = false; // GAP - no pagination at this point
+        const path_prefix = params?.path_prefix;
 
         const root_name = requesting_account.name.unwrap();
-        // CORE CHANGES PENDING - START
         const requesting_account_iam_users = _.filter(system_store.data.accounts, function(acc) {
             if (!acc.name.unwrap().includes(IAM_SPLIT_CHARACTERS)) {
                 return false;
             }
-            return acc.name.unwrap().split(IAM_SPLIT_CHARACTERS)[1] === root_name;
+            const is_owned = acc.name.unwrap().split(IAM_SPLIT_CHARACTERS)[1] === root_name;
+            if (!is_owned) return false;
+            
+            // Apply PathPrefix filter if specified
+            if (path_prefix) {
+                const user_path = acc.iam_path || IAM_DEFAULT_PATH;
+                return user_path.startsWith(path_prefix);
+            }
+            return true;
         });
         let members = _.map(requesting_account_iam_users, function(iam_user) {
             const member = {
                 user_id: iam_user._id.toString(),
                 iam_path: iam_user.iam_path || IAM_DEFAULT_PATH,
                 username: iam_user.name.unwrap().split(IAM_SPLIT_CHARACTERS)[0],
                 arn: iam_user.iam_arn,
-                // TODO: GAP Need to save created date
-                create_date: new Date(),
-                // TODO: GAP Miising password_last_used
-                password_last_used: Date.now(), // GAP
+                create_date: undefined, // Unknown until persisted
+                password_last_used: undefined,
             };
             return member;
         });
-        // CORE CHANGES PENDING - END
         members = members.sort((a, b) => a.username.localeCompare(b.username));

66-67: Remove hardcoded default_resource or make it configurable.

Line 67 hardcodes default_resource: 'noobaa-default-backing-store', which may not exist in all deployments and can cause account creation to fail. The TODO indicates this should be removed.

Either remove the default_resource field to let create_account resolve the default, or make it configurable through params. Consider applying:

                 iam_arn: iam_arn,
                 iam_path: params.iam_path,
                 role: 'iam_user',
-
-                // TODO: default_resource remove
-                default_resource: 'noobaa-default-backing-store',
             },

120-127: Only check for duplicate username when actually renaming.

Line 123 unconditionally checks if params.new_username already exists, but this should only run when a rename is actually requested (i.e., when params.new_username is defined and differs from the current username).

Apply this diff:

         const requested_account = system_store.get_account_by_email(username);
         let iam_path = requested_account.iam_path;
-        let user_name = requested_account.name.unwrap();
-        account_util._check_username_already_exists(action, { username: params.new_username }, requesting_account);
+        const current_username = account_util.get_iam_username(requested_account.name.unwrap());
+        let user_name = current_username;
+        
+        // Only check for duplicates if renaming
+        if (params.new_username !== undefined && params.new_username !== current_username) {
+            account_util._check_username_already_exists(action, { username: params.new_username }, requesting_account);
+        }
+        
         account_util._check_if_requested_account_is_root_account_or_IAM_user(action, requesting_account, requested_account);

105-109: Return persisted or undefined dates instead of "now".

Lines 106 and 108 return new Date() for create_date and password_last_used, which incorrectly suggests these events just happened. This misleads callers about when the user was actually created.

Apply this diff to use undefined or persisted values:

             arn: requested_account.iam_arn,
-            // TODO: GAP Need to save created date
-            create_date: new Date(),
-            // TODO: Dates missing : GAP
-            password_last_used: new Date(),
+            // Return undefined until these fields are persisted
+            create_date: undefined,
+            password_last_used: undefined,
         };

Alternatively, if a create_date field is being persisted on the account object, use requested_account.create_date instead.

🧹 Nitpick comments (3)
src/util/account_util.js (1)

555-567: Consider export naming convention for internal helpers.

Several functions prefixed with _ (e.g., _check_if_requesting_account_is_root_account, _returned_username) are exported, which diverges from the typical convention where _ indicates private/internal. This may reduce clarity about the public API surface.

Consider either:

  1. Removing the _ prefix for functions that are part of the public API
  2. Keeping these truly internal and only exporting the primary functions (create_account, delete_account, etc.)
  3. Documenting why these internal helpers need to be exported (e.g., for testing)
src/sdk/nb_account_sdk.js (1)

16-26: Consider using ES6 property shorthand for cleaner syntax.

Lines 21-22 explicitly assign rpc_client: rpc_client and internal_rpc_client: internal_rpc_client, which can be simplified using ES6 shorthand.

Apply this diff:

         super({
-            rpc_client: rpc_client,
-            internal_rpc_client: internal_rpc_client,
+            rpc_client,
+            internal_rpc_client,
             bucketspace: bucketspace,
             accountspace: accountspace,
         });
src/sdk/accountspace_nb.js (1)

10-10: Remove commented-out code.

Lines 10, 161, and 168 contain commented-out code that should be either implemented or removed to improve code clarity.

Consider removing:

  • Line 10: // const { account_cache } = require('./object_sdk');
  • Line 161: //const requesting_account = account_sdk.requesting_account;
  • Line 168: //const root_account = system_store.get_account_by_email(requesting_account.email);

If these are needed for future implementation, track them in a TODO or issue instead of leaving commented code.

Also applies to: 161-161, 168-168

📜 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 ddbfc2a and 2bc8354.

📒 Files selected for processing (9)
  • src/endpoint/endpoint.js (3 hunks)
  • src/endpoint/iam/iam_constants.js (2 hunks)
  • src/sdk/accountspace_nb.js (1 hunks)
  • src/sdk/nb_account_sdk.js (1 hunks)
  • src/server/system_services/account_server.js (4 hunks)
  • src/server/system_services/schemas/account_schema.js (1 hunks)
  • src/server/system_services/schemas/role_schema.js (1 hunks)
  • src/server/system_services/schemas/system_schema.js (2 hunks)
  • src/util/account_util.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/server/system_services/schemas/role_schema.js
  • src/endpoint/iam/iam_constants.js
  • src/endpoint/endpoint.js
  • src/server/system_services/schemas/account_schema.js
🧰 Additional context used
🧬 Code graph analysis (4)
src/sdk/nb_account_sdk.js (1)
src/endpoint/endpoint.js (7)
  • require (40-40)
  • require (41-41)
  • require (43-43)
  • require (44-44)
  • require (46-46)
  • NBAccountSDK (27-27)
  • internal_rpc_client (164-164)
src/sdk/accountspace_nb.js (4)
src/server/system_services/account_server.js (15)
  • _ (5-5)
  • require (13-13)
  • require (21-21)
  • require (22-22)
  • require (23-23)
  • SensitiveString (15-15)
  • account_util (25-25)
  • account_util (41-41)
  • dbg (12-12)
  • system_store (17-17)
  • params (315-315)
  • params (678-678)
  • params (942-942)
  • req (119-119)
  • req (383-383)
src/util/account_util.js (18)
  • _ (4-4)
  • require (8-8)
  • require (14-14)
  • require (17-17)
  • require (18-18)
  • SensitiveString (9-9)
  • dbg (7-7)
  • system_store (12-12)
  • IamError (15-15)
  • IamError (318-318)
  • IamError (358-358)
  • IamError (387-387)
  • IamError (424-424)
  • IamError (448-448)
  • req (536-536)
  • username (353-353)
  • username (383-384)
  • username (417-418)
src/sdk/nb_account_sdk.js (1)
  • AccountSpaceNB (6-6)
src/endpoint/iam/iam_constants.js (3)
  • IAM_ACTIONS (5-16)
  • IAM_DEFAULT_PATH (47-47)
  • IAM_SPLIT_CHARACTERS (61-61)
src/util/account_util.js (2)
src/endpoint/iam/ops/iam_list_signing_certificates.js (1)
  • requesting_account_name (26-27)
src/endpoint/iam/iam_constants.js (3)
  • IAM_SPLIT_CHARACTERS (61-61)
  • IAM_DEFAULT_PATH (47-47)
  • IAM_ACTIONS (5-16)
src/server/system_services/account_server.js (1)
src/sdk/accountspace_nb.js (1)
  • account_util (6-6)
⏰ 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-jest-unit-tests
  • GitHub Check: run-package-lock-validation
  • GitHub Check: Build Noobaa Image
🔇 Additional comments (3)
src/server/system_services/schemas/system_schema.js (1)

79-103: LGTM! Schema updates support IAM service.

The additions of 'iam' to both the service and api enums are consistent with the existing schema structure and align with the PR's goal of introducing IAM support.

src/server/system_services/account_server.js (1)

36-47: LGTM! Clean delegation to centralized account utilities.

The refactoring to delegate account creation, key generation, and deletion to account_util reduces code duplication and centralizes account management logic. The validation flow is preserved and the API surface remains consistent.

src/sdk/accountspace_nb.js (1)

15-21: Track outstanding TODOs for production readiness.

Multiple TODO comments throughout the file indicate incomplete functionality, including:

  • IAM account lifecycle questions (lines 15-21)
  • Hardcoded default_resource (lines 66-67)
  • Missing account cache invalidation (lines 76-77, 146-147, 179)
  • Missing event dispatching (lines 77, 147)
  • Missing IAM path persistence and dates (lines 101-102)
  • Missing access key and policy cleanup (lines 170-171)

Consider creating tracking issues for these items to ensure they are addressed before the feature reaches production, especially:

  1. The hardcoded default_resource which can cause failures
  2. The missing create_date persistence which breaks API contracts
  3. The access key/policy cleanup which can leave orphaned data

Also applies to: 66-67, 76-77, 101-102, 146-147, 170-171

Comment on lines 71 to 73
// CORE CHANGES PENDING - START
const iam_account = await account_util.create_account(req);
console.log("iam_account ====>>>", iam_account);
// CORE CHANGES PENDING - END
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Remove debug console.log statement.

Line 73 contains a console.log statement that should not be in production code. Use the dbg logger instead for debugging output.

Apply this diff:

-        // CORE CHANGES PENDING - START
         const iam_account = await account_util.create_account(req);
-        console.log("iam_account ====>>>", iam_account);
-        // CORE CHANGES PENDING - END
+        dbg.log1('IAM account created:', iam_account.id);
🤖 Prompt for AI Agents
In src/sdk/accountspace_nb.js around lines 71 to 74, remove the console.log
debug statement at line 73 and replace it with the project's dbg logger (e.g.,
dbg(...) or dbg.info(...)) to output the same iam_account information; ensure
the dbg logger is imported/available in this module and format the log message
consistently with other debug statements, then remove the stray comment markers
if any.

Comment on lines 113 to 134
async update_user(params, account_sdk) {
const action = IAM_ACTIONS.UPDATE_USER;
const requesting_account = system_store.get_account_by_email(account_sdk.requesting_account.email);
const username = account_util.populate_username(params.username, requesting_account.name.unwrap());
account_util._check_if_requesting_account_is_root_account(action, requesting_account,
{ username: params.username, iam_path: params.iam_path });
account_util._check_if_account_exists(action, params.username, requesting_account);
const requested_account = system_store.get_account_by_email(username);
let iam_path = requested_account.iam_path;
let user_name = requested_account.name.unwrap();
account_util._check_username_already_exists(action, { username: params.new_username }, requesting_account);
account_util._check_if_requested_account_is_root_account_or_IAM_user(action, requesting_account, requested_account);
account_util._check_if_requested_is_owned_by_root_account(action, requesting_account, requested_account);
if (params.new_iam_path !== undefined) iam_path = params.new_iam_path;
if (params.new_username !== undefined) user_name = params.new_username;
const iam_arn = iam_utils.create_arn_for_user(requested_account._id.toString(), user_name, iam_path);
const new_account_name = new SensitiveString(`${params.new_username}:${requesting_account.name.unwrap()}`);
const updates = {
name: new_account_name,
email: new_account_name,
iam_arn: iam_arn,
iam_path: iam_path,
};
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Fix username derivation in update_user to avoid "undefined:root".

Line 129 unconditionally uses params.new_username even when it's undefined, which would create an invalid account name like "undefined:root_account_name". The code should use the user_name variable which is conditionally updated on line 127.

Apply this diff to fix the username handling:

         let iam_path = requested_account.iam_path;
-        let user_name = requested_account.name.unwrap();
+        const current_username = account_util.get_iam_username(requested_account.name.unwrap());
+        let user_name = current_username;
@@
         if (params.new_iam_path !== undefined) iam_path = params.new_iam_path;
         if (params.new_username !== undefined) user_name = params.new_username;
         const iam_arn = iam_utils.create_arn_for_user(requested_account._id.toString(), user_name, iam_path);
-        const new_account_name = new SensitiveString(`${params.new_username}:${requesting_account.name.unwrap()}`);
+        const new_account_name = new SensitiveString(`${user_name}${IAM_SPLIT_CHARACTERS}${requesting_account.name.unwrap()}`);

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/sdk/accountspace_nb.js around lines 113 to 135, the code builds
new_account_name using params.new_username directly which can be undefined and
produce "undefined:root"; instead, use the already-determined user_name variable
(which was set to requested_account.name.unwrap() and conditionally overridden
when params.new_username exists). Change the construction of new_account_name to
use user_name (e.g. new
SensitiveString(`${user_name}:${requesting_account.name.unwrap()}`)) and update
the updates object to set name and email from that new_account_name (not
params.new_username).

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

♻️ Duplicate comments (5)
src/util/account_util.js (1)

507-511: Fix resource existence check - comparing object to number.

Line 509 compares req.system.pools_by_name (an object) to 0 (a number), which will always be false. This check is meant to catch the "no resources" condition but fails to do so.

Apply this diff:

         if (!req.rpc_params.new_system_parameters) {
-            if (req.system.pools_by_name === 0) {
+            if (!req.system.pools_by_name || Object.keys(req.system.pools_by_name).length === 0) {
                 throw new RpcError('No resources in the system - Can\'t create accounts');
             }
src/sdk/accountspace_nb.js (4)

112-154: Fix username derivation to avoid "undefined:root_account" bug.

Line 128 directly uses params.new_username which can be undefined, creating invalid account names like "undefined:root_account_name". The code should use the user_name variable that was conditionally updated on line 126.

Apply this diff:

         if (params.new_iam_path !== undefined) iam_path = params.new_iam_path;
         if (params.new_username !== undefined) user_name = params.new_username;
         const iam_arn = iam_utils.create_arn_for_user(requested_account._id.toString(), user_name, iam_path);
-        const new_account_name = new SensitiveString(`${params.new_username}:${requesting_account.name.unwrap()}`);
+        const new_account_name = new SensitiveString(`${user_name}${IAM_SPLIT_CHARACTERS}${requesting_account.name.unwrap()}`);

197-209: Fix date inconsistencies and consider PathPrefix filtering.

Line 206 uses Date.now() which returns a number (milliseconds), while line 204 uses new Date() which returns a Date object. Use new Date() consistently for both, or better yet, use undefined for password_last_used as suggested in get_user. Additionally, the list_users implementation doesn't filter by params.path_prefix, though past reviews suggest this was addressed - verify if PathPrefix filtering is required.

Apply this diff:

             const member = {
                 user_id: iam_user._id.toString(),
                 iam_path: iam_user.iam_path || IAM_DEFAULT_PATH,
                 username: iam_user.name.unwrap().split(IAM_SPLIT_CHARACTERS)[0],
                 arn: iam_user.iam_arn,
-                // TODO: GAP Need to save created date
-                create_date: new Date(),
-                // TODO: GAP Miising password_last_used
-                password_last_used: Date.now(), // GAP
+                // TODO: Add creation_date to schema
+                create_date: iam_user.creation_date || undefined,
+                password_last_used: undefined, // Not tracked; avoid false data
             };

66-67: Remove hardcoded default_resource to avoid failures.

The hardcoded 'noobaa-default-backing-store' may not exist in all deployments, causing account creation to fail. As noted in past reviews, let account_util.create_account resolve the default resource automatically, or pass through an explicit resource only if provided by the caller.

Apply this diff:

                 iam_arn: iam_arn,
                 iam_path: params.iam_path,
                 role: 'iam_user',
-
-                // TODO: default_resource remove
-                default_resource: 'noobaa-default-backing-store',
             },

104-107: Replace placeholder dates with actual persisted values or undefined.

Returning new Date() for create_date and password_last_used is misleading as it returns the current time, not when the account was created or password last used. As discussed in past reviews, either use persisted timestamps (e.g., requested_account.create_date if available) or return undefined for password_last_used to avoid returning false data.

Apply this diff:

             arn: requested_account.iam_arn,
-            // TODO: GAP Need to save created date
-            create_date: new Date(),
-            // TODO: Dates missing : GAP
-            password_last_used: new Date(),
+            // TODO: Add creation_date to schema; until then use undefined
+            create_date: requested_account.creation_date || undefined,
+            password_last_used: undefined, // Not tracked; return undefined to avoid false data
         };
🧹 Nitpick comments (5)
src/endpoint/iam/iam_constants.js (1)

61-61: Add documentation comment for IAM_SPLIT_CHARACTERS.

As noted in past reviews, a comment explaining this constant's purpose would improve clarity. The delimiter separates IAM usernames from root account names in composite account identifiers.

Apply this diff:

+// Delimiter used to construct composite account names: format is "username:root_account_name"
 const IAM_SPLIT_CHARACTERS = ':';
src/server/system_services/schemas/account_schema.js (1)

15-35: Consider adding creation_date field for IAM API compliance.

As discussed in past reviews, IAM's GetUser and ListUsers APIs require a create_date timestamp. While the team acknowledged the need to address upgrade scripts for existing accounts, adding this field to the schema would complete the IAM implementation. The current workaround uses new Date() placeholders which return incorrect values.

src/endpoint/endpoint.js (1)

437-441: Consider enabling stats collection for account SDK.

The stats parameter is commented out, likely because EndpointStatsCollector doesn't yet track account operations. Consider adding account-level metrics (create_user, delete_user, etc.) to the stats collector for observability, similar to object operations.

src/util/account_util.js (2)

142-148: Store create_date in the database rather than generating at return time.

Line 146 returns new Date() for IAM account creation time, but this generates the timestamp at function return rather than using the actual database insertion time. Consider storing creation_date in the account record during insertion (line 120-126) and returning that persisted value, ensuring consistency with the actual creation time.


428-450: Clarify ARN path source in access denied errors.

Line 432 uses requesting_account.path || IAM_DEFAULT_PATH, but the account schema defines iam_path, not path. Consider using requesting_account.iam_path || IAM_DEFAULT_PATH for consistency with the schema.

Apply this diff:

     const account_id_for_arn = _get_account_owner_id_for_arn(requesting_account);
     const arn_for_requesting_account = create_arn_for_user(account_id_for_arn,
-        requesting_account.name.unwrap(), requesting_account.path || IAM_DEFAULT_PATH);
+        requesting_account.name.unwrap(), requesting_account.iam_path || IAM_DEFAULT_PATH);
📜 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 2bc8354 and 590d6b6.

📒 Files selected for processing (9)
  • src/endpoint/endpoint.js (3 hunks)
  • src/endpoint/iam/iam_constants.js (2 hunks)
  • src/sdk/accountspace_nb.js (1 hunks)
  • src/sdk/nb_account_sdk.js (1 hunks)
  • src/server/system_services/account_server.js (4 hunks)
  • src/server/system_services/schemas/account_schema.js (1 hunks)
  • src/server/system_services/schemas/role_schema.js (1 hunks)
  • src/server/system_services/schemas/system_schema.js (2 hunks)
  • src/util/account_util.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/server/system_services/schemas/system_schema.js
  • src/sdk/nb_account_sdk.js
  • src/server/system_services/schemas/role_schema.js
🧰 Additional context used
🧬 Code graph analysis (3)
src/sdk/accountspace_nb.js (3)
src/util/account_util.js (17)
  • _ (4-4)
  • require (8-8)
  • require (14-14)
  • require (17-17)
  • require (18-18)
  • SensitiveString (9-9)
  • system_store (12-12)
  • IamError (15-15)
  • IamError (318-318)
  • IamError (358-358)
  • IamError (387-387)
  • IamError (424-424)
  • IamError (448-448)
  • req (536-536)
  • username (353-353)
  • username (383-384)
  • username (417-418)
src/sdk/nb_account_sdk.js (1)
  • AccountSpaceNB (6-6)
src/endpoint/iam/iam_constants.js (3)
  • IAM_ACTIONS (5-16)
  • IAM_DEFAULT_PATH (47-47)
  • IAM_SPLIT_CHARACTERS (61-61)
src/util/account_util.js (1)
src/endpoint/iam/iam_constants.js (3)
  • IAM_SPLIT_CHARACTERS (61-61)
  • IAM_DEFAULT_PATH (47-47)
  • IAM_ACTIONS (5-16)
src/server/system_services/account_server.js (1)
src/sdk/accountspace_nb.js (1)
  • account_util (6-6)
⏰ 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). (1)
  • GitHub Check: ceph-s3-tests / Ceph S3 Tests
🔇 Additional comments (6)
src/endpoint/endpoint.js (1)

27-27: LGTM!

The NBAccountSDK import follows the established pattern for SDK integrations and is correctly placed alongside ObjectSDK and StsSDK imports.

src/server/system_services/account_server.js (3)

36-47: LGTM!

The refactoring to delegate account creation to account_util is a solid improvement. It centralizes account management logic and reduces duplication, making the codebase more maintainable.


135-137: LGTM!

Delegating key generation to account_util maintains consistency with the refactored account creation flow and centralizes credential management logic.


402-406: LGTM!

The delegation pattern for delete_account is consistent with other refactored methods. Keeping the pre-flight checks (_verify_can_delete_account) in account_server while delegating the actual deletion maintains good separation of concerns.

src/util/account_util.js (2)

150-190: LGTM!

The delete_account implementation properly handles role cleanup and includes comprehensive audit logging for both success and failure cases.


304-310: LGTM!

The username helper functions (populate_username and get_iam_username) provide clear, reusable logic for handling composite IAM account names.

Comment on lines +30 to +35
owner: { objectid: true },
tagging: {
$ref: 'common_api#/definitions/tagging',
},
iam_arn: { type: 'string' },
iam_path: { type: 'string' },
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Ensure iam_arn stays synchronized with iam_path on updates.

The iam_arn is stored for performance (avoiding recalculation on every S3 request), but as noted in past reviews, it must be regenerated whenever iam_path or username changes. Verify that all update paths (e.g., UpdateUser) properly maintain this invariant.

Run this script to confirm update paths handle ARN regeneration:


🏁 Script executed:

#!/bin/bash
# Verify that update operations regenerate iam_arn when iam_path changes
rg -n -C5 --type=js '\biam_path\s*[=:]' | rg -C5 'iam_arn'

Length of output: 6782


🏁 Script executed:

#!/bin/bash
# Search for all account update operations to verify synchronization is maintained
rg -n --type=js 'system_store\.make_changes|update_user|update_account' -A10 | grep -A10 'iam_arn\|iam_path'

Length of output: 17593


🏁 Script executed:

#!/bin/bash
# Check for any direct field assignments to iam_path or iam_arn that might bypass regeneration
rg -n --type=js '\biam_path\s*=' -B3 -A3 | head -50

Length of output: 4789


🏁 Script executed:

#!/bin/bash
# Verify iam_utils.create_arn_for_user is used consistently for iam_arn generation
rg -n --type=js 'create_arn_for_user' -B2 -A2

Length of output: 9979


🏁 Script executed:

#!/bin/bash
# Get full update_user implementation in accountspace_fs.js to verify iam_arn regeneration
rg -n 'async update_user' -A40 src/sdk/accountspace_fs.js

Length of output: 2731


🏁 Script executed:

#!/bin/bash
# Check if iam_arn is regenerated or updated in config_fs operations
rg -n 'update_account_config_file' -B5 -A10 src/sdk/accountspace_fs.js

Length of output: 4596


🏁 Script executed:

#!/bin/bash
# Search for _update_account_config_new_username to see if ARN is handled there
rg -n '_update_account_config_new_username' -A30 src/sdk/accountspace_fs.js

Length of output: 3568


Synchronization bug in accountspace_fs.js: iam_arn not regenerated when iam_path changes.

The update_user operation in src/sdk/accountspace_fs.js (line 223) updates iam_path but fails to regenerate the stored iam_arn field before persisting to config file (line 229). This creates a stale iam_arn on disk that diverges from iam_path.

Compare with src/sdk/accountspace_nb.js (line 127), which explicitly regenerates iam_arn before saving. Ensure accountspace_fs.js regenerates iam_arn using create_arn_for_user() when iam_path is updated, similar to the accountspace_nb.js pattern.

🤖 Prompt for AI Agents
In src/sdk/accountspace_fs.js around lines 220 to 230, the update_user handler
updates iam_path but does not regenerate iam_arn before writing the config,
leaving iam_arn stale on disk; modify the flow so that when iam_path is changed
you call create_arn_for_user(user) (same pattern as in
src/sdk/accountspace_nb.js around line 127) to compute and set user.iam_arn,
then persist the updated user object to the config file; ensure this
regeneration occurs only when iam_path is actually updated and do the arn
assignment before the save operation.

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.

3 participants