Skip to content

Conversation

@nikhilsaikethe
Copy link
Contributor

@nikhilsaikethe nikhilsaikethe commented Aug 25, 2025

User description

  1. sanitize the user input for example if user enters any character other than alphanumeric , hyphen or space then we will restrict
  2. org identifier included in search options and also it will be now part of display as well org name | org identifier
  3. introduce org rename functionality so user can able to rename the existing orgs
  4. restricted the above rules at the api level as well @YashodhanJoshi1

PR Type

Enhancement, Bug fix


Description

  • Add organization rename functionality

  • Sanitize organization name input

  • Show name and identifier in menus/search

  • Enable edit action in organizations table


Diagram Walkthrough

flowchart LR
  UIList["Organizations table: edit action"] -- "opens" --> Drawer["AddUpdateOrganization dialog (update mode)"]
  Drawer -- "calls" --> Service["organizations.rename_organization()"]
  Drawer -- "sanitizes" --> Name["Organization name input"]
  MainLayout["Org selector/search"] -- "search by" --> Fields["label or identifier"]
  Cargo["Cargo.toml features"] -- "enable enterprise deps" --> Enterprise["o2_* crates"]
Loading

File Walkthrough

Relevant files
Enhancement
AddUpdateOrganization.vue
Sanitize input and implement org rename flow                         

web/src/components/iam/organizations/AddUpdateOrganization.vue

  • Add sanitized input utility for org name.
  • Support update flow via modelValue and ID field UI tweaks.
  • Use rename API when updating; create otherwise.
  • Minor cleanup and improve input appearance.
+35/-7   
ListOrganizations.vue
Add edit action and identifier-aware filtering                     

web/src/components/iam/organizations/ListOrganizations.vue

  • Add actions column with edit button.
  • Wire update dialog via route/query and pass model-value.
  • Include identifier in filtering; reset update state.
  • Success notification differentiates add vs update.
+110/-12
MainLayout.vue
Improve org menu display and search by identifier               

web/src/layouts/MainLayout.vue

  • Widen org menu list and text width.
  • Display "name | identifier" with truncation tooltip.
  • Enable search by label or identifier.
+13/-8   
organizations.ts
Introduce organization rename service endpoint                     

web/src/services/organizations.ts

  • Add rename_organization API method.
  • Send PUT to /api/{orgIdentifier}/rename with new_name.
+5/-0     
Configuration changes
Cargo.toml
Adjust features to enable enterprise stack                             

Cargo.toml

  • Enable enterprise features by default.
  • Add o2 enterprise-related optional dependencies.
  • Make cloud feature depend on enterprise.
+12/-3   

@github-actions github-actions bot added ☢️ Bug Something isn't working Review effort 3/5 labels Aug 25, 2025
@github-actions
Copy link
Contributor

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 3 🔵🔵🔵⚪⚪
🧪 No relevant tests
🔒 No security concerns identified
⚡ Recommended focus areas for review

Input Sanitization

The custom sanitizeInput decodes and re-encodes HTML entities, but may double-encode ampersands in already safe strings and alters whitespace (collapsing multiple spaces). Verify this won’t corrupt legitimate names, and consider using a well-vetted sanitizer or limiting to escaping on render.

const sanitizeInput = (val: string) => {
   // First decode any existing HTML entities
    const decoded = val
      .replace(/&/g, "&")
      .replace(/&lt;/g, "<")
      .replace(/&gt;/g, ">")
      .replace(/&quot;/g, '"')
      .replace(/&#039;/g, "'");

    // Then sanitize
    return decoded
      .trim()
      .replace(/&/g, "&amp;")
      .replace(/</g, "&lt;")
      .replace(/>/g, "&gt;")
      .replace(/"/g, "&quot;")
      .replace(/'/g, "&#039;")
      .replace(/\s+/g, " ");
  };
Identifier Assignment

When opening update dialog, identifier is set from to_be_updated_org_id (which appears to be the identifier itself). Confirm that id vs identifier are consistently used; otherwise, rename may target wrong org or display incorrect data.

};

watch(
  () => router.currentRoute.value.query?.action,
  (action) => {
    if (action == "add" && isCloudOrEnterprise()) {
      showAddOrganizationDialog.value = true;
    }
    else if (action == "update" && isCloudOrEnterprise()) {
      showAddOrganizationDialog.value = true;
      toBeUpdatedOrganization.value = {
        id: router.currentRoute.value.query?.to_be_updated_org_id || "",
        name: router.currentRoute.value.query?.to_be_updated_org_name || "",
        identifier: router.currentRoute.value.query?.to_be_updated_org_id || "",
      };
    }
  },
);

onMounted(() => {
  if (
    router.currentRoute.value.query.action == "add" &&
    isCloudOrEnterprise()
  ) {
    showAddOrganizationDialog.value = true;
  }
  else if (
    router.currentRoute.value.query.action == "update" &&
    isCloudOrEnterprise()
  ) {
    showAddOrganizationDialog.value = true;
    toBeUpdatedOrganization.value = {
      id: router.currentRoute.value.query?.to_be_updated_org_id || "",
      name: router.currentRoute.value.query?.to_be_updated_org_name || "",
      identifier: router.currentRoute.value.query?.to_be_updated_org_id || "",
    };
  }
});

onUpdated(() => {
  if (
    router.currentRoute.value.query.action == "add" &&
    isCloudOrEnterprise()
  ) {
    showAddOrganizationDialog.value = true;
  }
  else if (
    router.currentRoute.value.query.action == "update" &&
    isCloudOrEnterprise()
  ) {
    showAddOrganizationDialog.value = true;
    toBeUpdatedOrganization.value = {
      id: router.currentRoute.value.query?.to_be_updated_org_id || "",
      name: router.currentRoute.value.query?.to_be_updated_org_name || "",
      identifier: router.currentRoute.value.query?.to_be_updated_org_id || "",
    };
  }
Truncation Logic

The org menu now concatenates label and identifier and truncates based on label length only. Long identifiers may overflow; tooltip is only shown when label length > 30. Consider truncating and tooltipping based on combined display length.

  v-close-popup
  dense
  :class="{'text-primary': props.row.identifier === userClickedOrg?.identifier                            }"
  @click="selectedOrg = props.row; updateOrganization()"
>
  <q-item-section>
    <q-item-label
        data-test="organization-menu-item-label-item-label"
        class="tw-overflow-hidden tw-whitespace-nowrap tw-text-ellipsis tw-max-w-[430px]"
      >
        {{ props.row.label.length > 30 ? props.row.label.substring(0, 30) + '... | ' + props.row.identifier : props.row.label + ' | ' + props.row.identifier }}
        <q-tooltip v-if="props.row.label.length > 30"  anchor="bottom middle" self="top start">
          {{ props.row.label }}
        </q-tooltip>
      </q-item-label>
  </q-item-section>

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

Greptile Summary

This PR introduces three main organizational improvements to OpenObserve:

1. Input Sanitization for XSS Prevention
The PR adds HTML entity encoding to prevent XSS attacks when users input HTML tags like <h1>. A sanitizeInput function is implemented in AddUpdateOrganization.vue that first decodes existing HTML entities, then re-encodes them along with trimming whitespace. This ensures user input like <h1> gets converted to &lt;h1&gt; before being processed.

2. Enhanced Organization Display and Search
Organization dropdowns now show both name and identifier in the format "name | identifier" instead of just the name. The UI has been updated with wider containers (450px vs 250px) to accommodate this extended format. Search functionality is enhanced to filter by both organization name and identifier, making it easier for users to find organizations when managing multiple similar entries.

3. Organization Rename Functionality
A complete rename workflow has been added, allowing users to modify existing organization names. This includes:

  • New rename_organization API method in the organizations service
  • Edit buttons in the organization list for cloud/enterprise users
  • Route handling and state management for the update workflow
  • Reuse of the existing AddUpdateOrganization component for both create and update operations

Architectural Changes
The PR also enables enterprise features by default in Cargo.toml, adding dependencies for enterprise modules including authentication (Dex), authorization (OpenFGA), and rate limiting capabilities that support advanced organization management.

These changes work together to provide a more secure, user-friendly, and feature-complete organization management experience while maintaining the existing component architecture and patterns.

Confidence score: 3/5

  • This PR has mixed implementation quality with some solid functionality but notable code quality issues
  • Score reflects good security improvements and feature completeness offset by code duplication, inconsistent naming, and potential logic issues
  • Pay close attention to ListOrganizations.vue and AddUpdateOrganization.vue for code quality improvements

5 files reviewed, 2 comments

Edit Code Review Bot Settings | Greptile

});
this.showAddOrganizationDialog = false;
//after updating the organization we will reset the toBeUpdatedOrganization
const isUpdated = this.toBeUpdatedOrganization.id.length != 0;
Copy link
Contributor

Choose a reason for hiding this comment

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

style: Use strict equality (!==) instead of loose equality (!=) for better type safety.

Suggested change
const isUpdated = this.toBeUpdatedOrganization.id.length != 0;
const isUpdated = this.toBeUpdatedOrganization.id.length !== 0;

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks fixed that

Comment on lines 157 to 175
const sanitizeInput = (val: string) => {
// First decode any existing HTML entities
const decoded = val
.replace(/&amp;/g, "&")
.replace(/&lt;/g, "<")
.replace(/&gt;/g, ">")
.replace(/&quot;/g, '"')
.replace(/&#039;/g, "'");
// Then sanitize
return decoded
.trim()
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;")
.replace(/\s+/g, " ");
};
Copy link
Contributor

Choose a reason for hiding this comment

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

style: The sanitizeInput function properly prevents XSS by encoding HTML entities. However, applying sanitization after trimming at line 225 creates redundancy since sanitizeInput already trims.

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 we are doing it purposely for any spaces that are there after decoding

@github-actions
Copy link
Contributor

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
Possible issue
Harden input sanitization

Guard against nullish and non-string inputs to prevent runtime errors when fields
are empty or not yet initialized. Also cap the normalized length to a reasonable
maximum to avoid excessively long names slipping through.

web/src/components/iam/organizations/AddUpdateOrganization.vue [157-175]

-const sanitizeInput = (val: string) => {
-   // First decode any existing HTML entities
-    const decoded = val
-      .replace(/&amp;/g, "&")
-      .replace(/&lt;/g, "<")
-      .replace(/&gt;/g, ">")
-      .replace(/&quot;/g, '"')
-      .replace(/&#039;/g, "'");
+const sanitizeInput = (val: any) => {
+  const input = typeof val === 'string' ? val : '';
+  const decoded = input
+    .replace(/&amp;/g, "&")
+    .replace(/&lt;/g, "<")
+    .replace(/&gt;/g, ">")
+    .replace(/&quot;/g, '"')
+    .replace(/&#039;/g, "'");
+  const sanitized = decoded
+    .trim()
+    .replace(/&/g, "&amp;")
+    .replace(/</g, "&lt;")
+    .replace(/>/g, "&gt;")
+    .replace(/"/g, "&quot;")
+    .replace(/'/g, "&#039;")
+    .replace(/\s+/g, " ");
+  return sanitized.length > 200 ? sanitized.slice(0, 200) : sanitized;
+};
 
-    // Then sanitize
-    return decoded
-      .trim()
-      .replace(/&/g, "&amp;")
-      .replace(/</g, "&lt;")
-      .replace(/>/g, "&gt;")
-      .replace(/"/g, "&quot;")
-      .replace(/'/g, "&#039;")
-      .replace(/\s+/g, " ");
-  };
-
Suggestion importance[1-10]: 7

__

Why: Guarding against non-string inputs is reasonable and aligns with how sanitizeInput is used before form submission; adding a length cap can prevent excessive values, though the 200 limit is arbitrary. The improved_code accurately reflects the existing snippet with robust checks.

Medium
Fix create-vs-rename branching

Avoid mutating organizationData by deleting id before deciding the action; use a
local copy and a strict check for presence to prevent unintended updates. Also treat
null/undefined IDs as "create" to avoid calling rename with invalid identifiers.

web/src/components/iam/organizations/AddUpdateOrganization.vue [243-251]

-if (organizationId == "") {
-  callOrganization = organizationService.create(this.organizationData);
-}
-else {
+const organizationId = this.organizationData.id;
+const payload = { ...this.organizationData };
+delete payload.id;
+
+if (!organizationId) {
+  callOrganization = organizationService.create(payload);
+} else {
   callOrganization = organizationService.rename_organization(
     organizationId,
-    this.organizationData.name,
+    this.organizationData.name
   );
 }
Suggestion importance[1-10]: 6

__

Why: Using a local payload avoids mutating organizationData and a truthy check on organizationId is safer than == "". Impact is moderate; the current code works but is brittle for null/undefined IDs.

Low
General
Prevent duplicate actions column

Ensure the actions column is added only once; repeated mounts or reactive re-renders
can push duplicates, corrupting the table layout. Add a guard that checks if the
column already exists before pushing.

web/src/components/iam/organizations/ListOrganizations.vue [222-234]

-if(config.isCloud === "true" || config.isEnterprise === "true"){
-  columns.value.push(
-    {
+if ((config.isCloud === "true" || config.isEnterprise === "true") &&
+    !columns.value.some(col => col.name === "actions")) {
+  columns.value.push({
     name: "actions",
     field: "actions",
     label: t("user.actions"),
     align: "center",
     sortable: false,
     classes: "actions-column",
     style: "width: 67px;",
-  }
-)
+  });
 }
Suggestion importance[1-10]: 7

__

Why: Adding a guard to avoid pushing the "actions" column multiple times improves robustness against repeated reactive executions. The improved_code directly applies to the existing block and prevents UI duplication.

Medium

@nikhilsaikethe nikhilsaikethe force-pushed the fix/organization-issues branch 6 times, most recently from e6b98a9 to 6d5eaad Compare August 31, 2025 08:09
@nikhilsaikethe nikhilsaikethe force-pushed the fix/organization-issues branch 8 times, most recently from 5471375 to 86712e9 Compare September 8, 2025 04:15
return Err(anyhow::anyhow!("Not allowed to rename org"));
}

let has_valid_chars = name
Copy link
Contributor

Choose a reason for hiding this comment

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

name validation code seems repeated. It could be moved out into its own validation function. fn validate_org_name(s: &str) -> bool or fn validate_name(s: &str) -> bool or something similar.

nikhilsaikethe and others added 3 commits September 9, 2025 19:34
fix: org identifier should be included in search options

fix: organization input name sanitized

fix: show both name and identifier in org table

fix: introduce org update feature

fix: unit test case

fix: increase width to show id as well

fix: wait until org name visible

fix: add update santize input fixed
Signed-off-by: Yashodhan Joshi <[email protected]>
@nikhilsaikethe nikhilsaikethe force-pushed the fix/organization-issues branch from dde6bba to 39440dd Compare September 9, 2025 14:04
@hengfeiyang hengfeiyang merged commit b863826 into main Sep 9, 2025
25 of 26 checks passed
@hengfeiyang hengfeiyang deleted the fix/organization-issues branch September 9, 2025 14:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

☢️ Bug Something isn't working Review effort 3/5

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants