Skip to content

UN-2776 [FIX] Fixed GDrive connector showing always authenticated in workflow settings#1508

Merged
kirtimanmishrazipstack merged 8 commits into
mainfrom
UN-2776-gdrive-always-authenticated-inside-workflow
Sep 1, 2025
Merged

UN-2776 [FIX] Fixed GDrive connector showing always authenticated in workflow settings#1508
kirtimanmishrazipstack merged 8 commits into
mainfrom
UN-2776-gdrive-always-authenticated-inside-workflow

Conversation

@kirtimanmishrazipstack

@kirtimanmishrazipstack kirtimanmishrazipstack commented Aug 28, 2025

Copy link
Copy Markdown
Contributor

What

  • Fix GDrive connector showing always authenticated in workflow settings

Why

  • While gdrive oauth is successful, we are storing oauth-cachekey cookie to local storage
  • Although we have a centralized connector, but still we didn't segregate the oauth-cachekey based on workflow-id and connector type
  • Due to this if we have an existing gdrive connector which is authenticated, selecting a new connector would also make it authenticated accross workflows and between source and destination

How

  • Deleting cache from local-storage, and redis cache (already happeing)

Frontend change:

For new connector:

  • We should show Signin when user tries to do oauth.

For existing connector:

  • We should show reauthenticate when user tries to do oauth.

For both connectors:

  • When we receive the cache key, using it we can do test-connection and submit
  • Once submitted or modal is closed, we would remove from local storage and cache.

Backend change:

  • Handle the scenario when user tries to submit connector with same name. Backend throws server error. But we have deleted cache.
  • Although there is successful test-connection, still we need to reauthenticate again. This is a bug.
  • Handled this deleting the oauth cache key only when connector is added/updated.

Can this PR break any existing features. If yes, please list possible items. If no, please explain why. (PS: Admins do not merge the PR without this section filled)

Database Migrations

  • N/A

Env Config

  • N/A

Relevant Docs

Related Issues or PRs

Dependencies Versions

Notes on Testing

Screenshots

Checklist

I have read and understood the Contribution Guidelines.

@coderabbitai

coderabbitai Bot commented Aug 28, 2025

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • New Features

    • OAuth button now shows “Reauthenticate” for existing connectors; otherwise “Authenticate with Google”.
    • Per-connector OAuth status handling improves accuracy across connectors.
  • Bug Fixes

    • Cleans up OAuth cache and local storage after successful create/update to prevent stale authentication states.
    • More explicit error shown when OAuth is required before continuing.
  • Chores

    • Improved event tracking and URL handling integration for OAuth flows.
  • Refactor

    • Streamlined OAuth state management and status propagation between components.

Walkthrough

Adds per-connector OAuth status and cache scoping on the frontend, passes an isExistingConnector flag to OAuth UI, and clears connector-scoped OAuth keys on unmount and after successful submit; backend now cleans cached OAuth credentials after create/update and tightens missing-OAuth error handling.

Changes

Cohort / File(s) Summary of changes
Configure DS flow
frontend/src/components/input-output/configure-ds/ConfigureDs.jsx
Derives isExistingConnector from editItemId and OAuth metadata and passes it to OAuthDs. Cleans connector-scoped localStorage keys (oauth-cachekey-${selectedSourceId}, oauth-status-${selectedSourceId}) on unmount and clears them after successful OAuth-enabled submit. Adds usePostHogEvents and useRequestUrl imports.
OAuth DS components
frontend/src/components/oauth-ds/oauth-ds/OAuthDs.jsx, frontend/src/components/oauth-ds/google/GoogleOAuthButton.jsx
OAuthDs: adds isExistingConnector prop, uses per-connector oauthStatusKey = oauth-status-${selectedSourceId} for init, storage events, and parent setStatus; integrates useExceptionHandler; passes dynamic buttonText to Google button; updates effects and PropTypes. GoogleOAuthButton: adds optional buttonText prop (default "Authenticate with Google"); displays "Authenticated" on success, otherwise uses buttonText; updates PropTypes.
Backend OAuth cache handling
backend/connector_v2/views.py
_get_connector_metadata improved error handling/log messages for missing OAuth key. Added _cleanup_oauth_cache(connector_id: str) to delete cached OAuth creds (get_oauth_creds_from_cache(..., delete_key=True)) with guarded logging. perform_create and perform_update invoke cleanup after successful operations.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant User
  participant ConfigureDs
  participant OAuthDs
  participant GoogleBtn as GoogleOAuthButton
  participant Browser as localStorage/sessionStorage
  participant Google as OAuth Provider
  participant Backend as Backend API
  participant Cache as OAuth Cache

  User->>ConfigureDs: Open connector (selectedSourceId)
  ConfigureDs->>OAuthDs: Render with isExistingConnector, selectedSourceId
  OAuthDs->>Browser: Read oauth-status-${id}
  Note over OAuthDs,Browser: Subscribes to storage events for oauth-status-${id}

  User->>GoogleBtn: Click OAuth
  GoogleBtn->>OAuthDs: handleOAuth()
  OAuthDs->>Browser: sessionStorage set oauth-current-connector = ${id}
  OAuthDs->>Google: Open OAuth authorize window

  Google-->>Backend: OAuth callback with code
  Backend->>Cache: Store creds under oauth-cachekey-${id}
  Backend-->>Browser: Update localStorage oauth-status-${id} = success
  Note over Browser,OAuthDs: storage event updates status in component and parent

  User->>ConfigureDs: Submit connector (create/update)
  ConfigureDs->>Backend: POST/PUT with oauth_key
  Backend->>Cache: Read creds (delete_key=False)
  Backend-->>ConfigureDs: 2xx response
  Backend->>Cache: Cleanup creds (delete_key=True)
  ConfigureDs->>Browser: Remove oauth-cachekey-${id}, oauth-status-${id}

  opt Unmount
    ConfigureDs->>Browser: Cleanup oauth keys for ${id}
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes


📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Cache: Disabled due to Reviews > Disable Cache setting

Knowledge Base: Disabled due to Reviews > Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between e236044 and 41ac536.

📒 Files selected for processing (2)
  • frontend/src/components/oauth-ds/google/GoogleOAuthButton.jsx (2 hunks)
  • frontend/src/components/oauth-ds/oauth-ds/OAuthDs.jsx (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • frontend/src/components/oauth-ds/oauth-ds/OAuthDs.jsx
  • frontend/src/components/oauth-ds/google/GoogleOAuthButton.jsx
⏰ 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: build
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch UN-2776-gdrive-always-authenticated-inside-workflow

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Comment thread frontend/src/components/input-output/configure-ds/ConfigureDs.jsx Outdated
@kirtimanmishrazipstack kirtimanmishrazipstack changed the title UN-2776 [FIX] Fixed Google Drive connector showing always authenticated in workflow settings UN-2776 [FIX] Fixed GDrive connector showing always authenticated in workflow settings Aug 28, 2025

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
frontend/src/components/oauth-ds/oauth-status/OAuthStatus.jsx (1)

14-22: Use localStorage (not sessionStorage) for cross-window context; popup cannot read parent sessionStorage

sessionStorage is scoped per-tab/window and isn’t available to the OAuth popup. As a result, OAuthStatus will often find null currentConnector and never persist the status. Switch to localStorage for the "oauth-current-connector" handshake so the popup can read and clear it. Also fix the comment to avoid confusion.

-  // Set status for the workflow-connector that initiated OAuth (stored in sessionStorage for callback)
-  const currentConnector = sessionStorage.getItem("oauth-current-connector");
+  // Set status for the workflow-connector that initiated OAuth (stored in localStorage for cross-window callback)
+  const currentConnector = localStorage.getItem("oauth-current-connector");
   if (currentConnector) {
-    // currentConnector now contains workflowId-connectorType-sourceId format
+    // currentConnector contains workflowId-connectorType-sourceId format
     const statusKey = `oauth-status-${currentConnector}`;
     localStorage.setItem(statusKey, status);
-    // Clear the session storage after use
-    sessionStorage.removeItem("oauth-current-connector");
+    // Clear the local storage after use
+    localStorage.removeItem("oauth-current-connector");
   }
🧹 Nitpick comments (2)
frontend/src/components/oauth-ds/oauth-ds/OAuthDs.jsx (2)

31-39: Filter storage events by key to avoid unnecessary reads

Listen only to oauthStatusKey changes; use the event payload instead of rereading localStorage.

-  useEffect(() => {
-    const handleStorageChange = () => {
-      // Listen for changes to our specific workflow-connector combination only
-      const updatedOAuthStatus = localStorage.getItem(oauthStatusKey);
+  useEffect(() => {
+    const handleStorageChange = (e) => {
+      // Listen only for our specific workflow-connector status key
+      if (e.key !== oauthStatusKey) return;
+      const updatedOAuthStatus = e.newValue;
       if (updatedOAuthStatus) {
         setOAuthStatus(updatedOAuthStatus);
         setStatus(updatedOAuthStatus);
       }
     };

115-122: Constrain connectorType PropType to known values

This tightens the contract and prevents silent key drift.

   selectedSourceId: PropTypes.string.isRequired,
-  workflowId: PropTypes.string.isRequired,
-  connectorType: PropTypes.string.isRequired,
+  workflowId: PropTypes.string.isRequired,
+  connectorType: PropTypes.oneOf(["source", "destination"]).isRequired,
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Cache: Disabled due to Reviews > Disable Cache setting

Knowledge Base: Disabled due to Reviews > Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between 201d144 and f14fb5d.

📒 Files selected for processing (3)
  • frontend/src/components/input-output/configure-ds/ConfigureDs.jsx (4 hunks)
  • frontend/src/components/oauth-ds/oauth-ds/OAuthDs.jsx (3 hunks)
  • frontend/src/components/oauth-ds/oauth-status/OAuthStatus.jsx (1 hunks)
⏰ 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: build
🔇 Additional comments (5)
frontend/src/components/oauth-ds/oauth-ds/OAuthDs.jsx (2)

22-25: Good scoping of OAuth keys to workflow + connector type + sourceId

This should resolve cross-connector contamination in a workflow.


49-54: LGTM on initial status bootstrapping

Reading the workflow/connector-scoped status on mount matches the new key strategy.

frontend/src/components/input-output/configure-ds/ConfigureDs.jsx (3)

53-57: Per-workflow/per-connector keys: good

Key structure matches OAuthDs and should prevent cross-connector contamination.


361-371: Passing workflowId and connectorType down is correct

This aligns ConfigureDs with the new OAuthDs interface.


41-41: Confirm whether projectId should be aliased to workflowId
The workflow store only exposes projectId; ensure this aligns with your intended workflow scoping or introduce a dedicated workflowId to avoid state collisions.

Comment thread frontend/src/components/oauth-ds/oauth-ds/OAuthDs.jsx
Comment thread frontend/src/components/oauth-ds/oauth-ds/OAuthDs.jsx
@kirtimanmishrazipstack kirtimanmishrazipstack marked this pull request as draft August 29, 2025 05:31
@kirtimanmishrazipstack kirtimanmishrazipstack marked this pull request as ready for review August 29, 2025 15:17

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
frontend/src/components/input-output/configure-ds/ConfigureDs.jsx (1)

193-199: Bug: oauth-key attached only to connector_metadata.

Adapter OAuth tests will miss the key. Use the correct metadata bucket based on isConnector.

-    if (oAuthProvider?.length > 0) {
-      body["connector_metadata"] = {
-        ...body["connector_metadata"],
-        ...{ "oauth-key": cacheKey },
-      };
-    }
+    if (oAuthProvider?.length > 0) {
+      const metaKey = isConnector ? "connector_metadata" : "adapter_metadata";
+      body[metaKey] = {
+        ...body[metaKey],
+        "oauth-key": cacheKey,
+      };
+    }
♻️ Duplicate comments (1)
frontend/src/components/input-output/configure-ds/ConfigureDs.jsx (1)

54-58: Key scope outside workflows: verify selectedSourceId is always present.

If selectedSourceId is empty when adding from outside a workflow, localStorage keys become shared (e.g., oauth-cachekey-undefined). Provide a fallback scope.

-  const isExistingConnector = Boolean(editItemId || hasOAuthCredentials);
+  const isExistingConnector = Boolean(editItemId || hasOAuthCredentials);
+  // Ensure per-connector key scoping even when selectedSourceId is absent
+  const keyScope =
+    selectedSourceId ||
+    `${(type || 'unknown').toLowerCase()}-${oAuthProvider || 'oauth'}`;
+  const oauthCacheKey = `oauth-cachekey-${keyScope}`;
+  const oauthStatusKey = `oauth-status-${keyScope}`;

Run to check call sites pass a non-empty selectedSourceId in non-workflow flows:

#!/bin/bash
# Inspect ConfigureDs usages and props passed for selectedSourceId
rg -nP -C2 '<ConfigureDs\b' --type=js --type=jsx --type=ts --type=tsx
🧹 Nitpick comments (5)
frontend/src/components/oauth-ds/google/GoogleOAuthButton.jsx (3)

8-8: Fix label typo: "Sign in" (with space) for consistency.

Default should read "Sign in" per UX copy elsewhere and PR description.

-const GoogleOAuthButton = ({ handleOAuth, status, buttonText = "Signin" }) => {
+const GoogleOAuthButton = ({ handleOAuth, status, buttonText = "Sign in" }) => {

10-16: Remove derived state; compute label inline.

Avoiding useState/useEffect here simplifies and prevents stale text.

-  const [text, setText] = useState("");
-  useEffect(() => {
-    if (status === "success") {
-      setText("Authenticated");
-      return;
-    }
-    setText(buttonText);
-  }, [status, buttonText]);
+  const text = status === "success" ? "Authenticated" : buttonText;

Additionally, if desired, disable the button when authenticated:

-      <GoogleLoginButton onClick={handleOAuth}>
+      <GoogleLoginButton onClick={handleOAuth} disabled={status === "success"}>

30-30: Optional: tighten PropTypes for status.

If statuses are finite, consider oneOf([...]) to catch regressions.

-GoogleOAuthButton.propTypes = {
-  handleOAuth: PropTypes.func.isRequired,
-  status: PropTypes.string,
-  buttonText: PropTypes.string,
-};
+GoogleOAuthButton.propTypes = {
+  handleOAuth: PropTypes.func.isRequired,
+  status: PropTypes.string, // e.g., one of: '', 'success', 'error', 'pending'
+  buttonText: PropTypes.string,
+};
frontend/src/components/input-output/configure-ds/ConfigureDs.jsx (1)

352-358: Avoid mutating session adapters array.

Mutating store-derived arrays can cause subtle state bugs; create a new array.

-    const adaptersList = sessionDetails?.adapters;
-    if (adaptersList && !adaptersList.includes(adapterType)) {
-      adaptersList.push(adapterType);
-      const adaptersListInSession = { adapters: adaptersList };
-      updateSessionDetails(adaptersListInSession);
-    }
+    const current = Array.isArray(sessionDetails?.adapters)
+      ? sessionDetails.adapters
+      : [];
+    if (!current.includes(adapterType)) {
+      updateSessionDetails({ adapters: [...current, adapterType] });
+    }
backend/connector_v2/views.py (1)

89-89: Reduce log duplication.

“Fetching oauth data …” (Line 83) and “Using OAuth cache key …” communicate similar info. Keep one (ideally the latter) or lower the first to debug.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Cache: Disabled due to Reviews > Disable Cache setting

Knowledge Base: Disabled due to Reviews > Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between f14fb5d and 712bc5b.

📒 Files selected for processing (4)
  • backend/connector_v2/views.py (3 hunks)
  • frontend/src/components/input-output/configure-ds/ConfigureDs.jsx (5 hunks)
  • frontend/src/components/oauth-ds/google/GoogleOAuthButton.jsx (2 hunks)
  • frontend/src/components/oauth-ds/oauth-ds/OAuthDs.jsx (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • frontend/src/components/oauth-ds/oauth-ds/OAuthDs.jsx
⏰ 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: build
🔇 Additional comments (6)
frontend/src/components/input-output/configure-ds/ConfigureDs.jsx (3)

7-9: LGTM: imports added for analytics and URL util.


336-339: LGTM: clearing OAuth keys after successful submit.

Matches the PR objective; prevents leakage across connectors.


369-370: Confirm downstream usage of isExistingConnector for button label.

Ensure OAuthDs maps isExistingConnector → buttonText ("Reauthenticate" vs "Sign in") and propagates it to GoogleOAuthButton.

backend/connector_v2/views.py (3)

136-138: LGTM: cleanup runs only after a successful update.

Assuming _cleanup_oauth_cache is hardened as above, this won’t impact the happy path even if the cache is already cleared.


155-157: LGTM: cleanup runs only after a successful create.

Matches the front-end flow; ensures single-use cache keys.


85-89: Use provider-agnostic error text in MissingParamException

Apply this diff in backend/connector_v2/views.py (lines 85–89):

-            if not oauth_key:
-                raise MissingParamException(
-                    "OAuth authentication required. Please sign in with Google first."
-                )
+            if not oauth_key:
+                raise MissingParamException(
+                    "OAuth authentication required. Please complete OAuth sign-in first."
+                )

Confirm that clients aren’t relying on a separate param field in the error payload; if they do, continue using the param= kwarg (e.g. MissingParamException(param="oauth_key")) or adjust the exception’s signature accordingly.

Comment thread backend/connector_v2/views.py Outdated
Comment thread backend/connector_v2/views.py
Comment thread frontend/src/components/input-output/configure-ds/ConfigureDs.jsx
Comment thread backend/connector_v2/views.py
Comment thread frontend/src/components/oauth-ds/google/GoogleOAuthButton.jsx Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
frontend/src/components/oauth-ds/oauth-ds/OAuthDs.jsx (1)

74-81: Guard missing cache_key before proceeding

If the API doesn’t return a valid cache_key, the flow will open a broken URL and set bad state.

-const cacheKey = response?.data?.cache_key;
-const encodedCacheKey = encodeURIComponent(cacheKey);
-setCacheKey(cacheKey);
+const cacheKey = response?.data?.cache_key;
+if (!cacheKey || typeof cacheKey !== "string") {
+  throw new Error("OAuth cache key missing from response");
+}
+const encodedCacheKey = encodeURIComponent(cacheKey);
+setCacheKey(cacheKey);
 
 // Persist cache key to localStorage
 localStorage.setItem(oauthCacheKey, cacheKey);
♻️ Duplicate comments (3)
backend/connector_v2/views.py (2)

85-89: Make error message provider-agnostic.

Remove "Google" to keep copy generic and future-proof.

-            if not oauth_key:
-                raise MissingParamException(
-                    "OAuth authentication required. Please sign in with Google first."
-                )
+            if not oauth_key:
+                raise MissingParamException(
+                    "OAuth authentication required. Please sign in first."
+                )

89-95: Handle cache miss explicitly; avoid relying on None-check only.

Wrap the cache fetch to surface a friendly, consistent error and keep a defensive None guard.

-            logger.info(f"Using OAuth cache key for {connector_id}")
-            connector_metadata = ConnectorAuthHelper.get_oauth_creds_from_cache(
-                cache_key=oauth_key,
-                delete_key=False,  # Don't delete yet - wait for successful operation
-            )
-            if connector_metadata is None:
-                raise MissingParamException(param=ConnectorAuthKey.OAUTH_KEY)
+            logger.info(f"Using OAuth cache key for {connector_id}")
+            try:
+                connector_metadata = ConnectorAuthHelper.get_oauth_creds_from_cache(
+                    cache_key=oauth_key,
+                    delete_key=False,  # Don't delete yet - wait for successful operation
+                )
+            except CacheMissException:
+                raise CacheMissException("OAuth session expired. Please sign in again.")
+            if connector_metadata is None:  # Defensive in case the helper returns None
+                raise CacheMissException("OAuth session expired. Please sign in again.")
frontend/src/components/oauth-ds/oauth-ds/OAuthDs.jsx (1)

66-68: Use localStorage for cross-window OAuth context (popup cannot read sessionStorage)

The OAuth popup cannot access sessionStorage of the opener tab; use localStorage so the popup can read/clear the context.

-// Store connector context in sessionStorage for OAuth callback (survives window.open)
-sessionStorage.setItem("oauth-current-connector", selectedSourceId);
+// Store connector context in localStorage so the popup can read it
+localStorage.setItem("oauth-current-connector", selectedSourceId);

Optional: include provider (and workflow/type if available) for stronger disambiguation:

-localStorage.setItem("oauth-current-connector", selectedSourceId);
+localStorage.setItem("oauth-current-connector", `${oAuthProvider}-${selectedSourceId}`);
🧹 Nitpick comments (5)
frontend/src/components/oauth-ds/oauth-ds/OAuthDs.jsx (5)

21-24: Key names: confirm uniqueness; consider scoping by provider

Right now keys are scoped by selectedSourceId only. If IDs aren’t globally unique across workflows/types, collisions can still occur. Consider including oAuthProvider (and, if available, workflow/connector type) in the key.

Example change:

-const oauthCacheKey = `oauth-cachekey-${selectedSourceId}`;
-const oauthStatusKey = `oauth-status-${selectedSourceId}`;
+const oauthCacheKey = `oauth-cachekey-${oAuthProvider}-${selectedSourceId}`;
+const oauthStatusKey = `oauth-status-${oAuthProvider}-${selectedSourceId}`;

25-26: Copy: align with PR wording

PR says: new connector should show “Sign in”. Suggest:

-const buttonText = isExistingConnector ? "Reauthenticate" : "Authenticate with Google";
+const buttonText = isExistingConnector ? "Reauthenticate" : "Sign in with Google";

51-56: Initialize status unconditionally to reflect cleared state

Ensure cleared/absent status resets the UI.

-const connectorStatus = localStorage.getItem(oauthStatusKey);
-if (connectorStatus) {
-  setStatus(connectorStatus);
-  setOAuthStatus(connectorStatus);
-}
+const connectorStatus = localStorage.getItem(oauthStatusKey);
+setStatus(connectorStatus);
+setOAuthStatus(connectorStatus);

86-91: Optional: add noopener for the popup

Mitigates reverse tabnabbing. Use if the popup doesn’t rely on window.opener.

-window.open(
+window.open(
   url,
   "_blank",
-  "toolbar=yes,scrollbars=yes,resizable=yes,top=200,left=500,width=500,height=600"
+  "toolbar=yes,scrollbars=yes,resizable=yes,top=200,left=500,width=500,height=600,noopener"
 );

112-118: Tighten PropTypes (required where used)

oAuthProvider, setCacheKey, and setStatus appear mandatory for correct behavior.

-OAuthDs.propTypes = {
-  oAuthProvider: PropTypes.string,
-  setCacheKey: PropTypes.func,
-  setStatus: PropTypes.func,
-  selectedSourceId: PropTypes.string.isRequired,
-  isExistingConnector: PropTypes.bool,
-};
+OAuthDs.propTypes = {
+  oAuthProvider: PropTypes.string.isRequired,
+  setCacheKey: PropTypes.func.isRequired,
+  setStatus: PropTypes.func.isRequired,
+  selectedSourceId: PropTypes.string.isRequired,
+  isExistingConnector: PropTypes.bool,
+};
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Cache: Disabled due to Reviews > Disable Cache setting

Knowledge Base: Disabled due to Reviews > Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between 712bc5b and e236044.

📒 Files selected for processing (3)
  • backend/connector_v2/views.py (3 hunks)
  • frontend/src/components/oauth-ds/google/GoogleOAuthButton.jsx (2 hunks)
  • frontend/src/components/oauth-ds/oauth-ds/OAuthDs.jsx (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • frontend/src/components/oauth-ds/google/GoogleOAuthButton.jsx
⏰ 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: build
🔇 Additional comments (5)
backend/connector_v2/views.py (3)

100-120: Good: resilient, idempotent OAuth cache cleanup.

Catches cache-miss and swallows failures after logging; aligns with post-success cleanup semantics.


142-144: Good: cleanup after update.

Ensures the cache key is cleared only after successful update.


161-163: Good: cleanup after create.

Clears the cache post-success to prevent phantom “authenticated” states.

frontend/src/components/oauth-ds/oauth-ds/OAuthDs.jsx (2)

7-7: Good: centralized exception handling

Importing and using useExceptionHandler for OAuth errors is a solid improvement.


100-104: GoogleOAuthButton wiring looks correct

Passing status and buttonText is consistent and clear.

Comment thread frontend/src/components/oauth-ds/oauth-ds/OAuthDs.jsx
Comment thread frontend/src/components/oauth-ds/oauth-ds/OAuthDs.jsx
@github-actions

github-actions Bot commented Sep 1, 2025

Copy link
Copy Markdown
Contributor
filepath function $$\textcolor{#23d18b}{\tt{passed}}$$ SUBTOTAL
$$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ $$\textcolor{#23d18b}{\tt{test\_logs}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ $$\textcolor{#23d18b}{\tt{test\_cleanup}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ $$\textcolor{#23d18b}{\tt{test\_cleanup\_skip}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ $$\textcolor{#23d18b}{\tt{test\_client\_init}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ $$\textcolor{#23d18b}{\tt{test\_get\_image\_exists}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ $$\textcolor{#23d18b}{\tt{test\_get\_image}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ $$\textcolor{#23d18b}{\tt{test\_get\_container\_run\_config}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ $$\textcolor{#23d18b}{\tt{test\_get\_container\_run\_config\_without\_mount}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ $$\textcolor{#23d18b}{\tt{test\_run\_container}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ $$\textcolor{#23d18b}{\tt{test\_get\_image\_for\_sidecar}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ $$\textcolor{#23d18b}{\tt{test\_sidecar\_container}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{TOTAL}}$$ $$\textcolor{#23d18b}{\tt{11}}$$ $$\textcolor{#23d18b}{\tt{11}}$$

@sonarqubecloud

sonarqubecloud Bot commented Sep 1, 2025

Copy link
Copy Markdown

@kirtimanmishrazipstack kirtimanmishrazipstack merged commit d34f6b8 into main Sep 1, 2025
7 checks passed
@kirtimanmishrazipstack kirtimanmishrazipstack deleted the UN-2776-gdrive-always-authenticated-inside-workflow branch September 1, 2025 09:41
kirtimanmishrazipstack added a commit that referenced this pull request May 30, 2026
The AgenticProject descriptor in SHAREABLE_RESOURCES hardcoded
name_field="project_name"/id_field="project_id", copied from the CustomTool
tool_id/tool_name pattern. The real cloud model (agentic_studio_v1.AgenticProject,
cloud PR #1508) uses ORM fields "id" (UUID PK) and "name"; project_id/
project_name exist only as dict keys in get_pipeline_status(). On cloud this made
the group blast-radius view run values_list("project_id","project_name") ->
FieldError/500. Pure OSS was unaffected (app absent, skipped via LookupError).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
chandrasekharan-zipstack pushed a commit that referenced this pull request Jun 2, 2026
* init changes. User's Group's

* fix migration M2M. Agenctic Prompt Studio align with other resources

* Resource Share Modal UI fix

* users can't access a resource, if admin has removed that user from a group, and the resource has been shared to that group

* Removing idp group import, UI Fix

* Removing idp group import

* Sonar fix

* UN-2977 [FIX] address Greptile/CodeRabbit review on group sharing

- group list: validate ?member= as int (400 not 500)
- group views: reference IsOrgAdminForWrite.message, not list index
- sharing_helpers: cast UUID ids in list_resources_shared_with_group
  (fixes GET /groups/{pk}/resources/ crash); treat org-less user as
  non-admin without error-logging noise
- share serializer mixin: make model save + group-share write atomic
- adapter: move default-adapter cleanup to the share action where
  unsharing actually happens (shared_users is read-only on PATCH)
- frontend: guard group id on edit, orgId before replaceAll, TopBar
  search key for non-user rows, /groups nav active state, keep share
  modal open on failure

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* UN-2977 [FIX] validate remove_member user_id + configurable TopBar placeholder

- group_views.remove_member: guard non-numeric user_id (400 not 500), mirroring the list action
- TopBar: searchPlaceholder prop (default "Search Users"); Groups passes "Search Groups"

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* UN-2977 [FIX] Restrict group resources endpoint to org admins

The /groups/{pk}/resources/ action is the delete blast-radius view used
only by the admin delete-confirm flow, but IsOrgAdminForWrite permits GET
for any authenticated org member. That let non-members enumerate the
names/UUIDs of resources shared with groups they are not in, contradicting
the model where org admin has no implicit resource access. Gate the action
to org admins, mirroring the existing members POST / remove_member guards.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* UN-2977 [FIX] Drop stale IdP group-sync leftovers from sample.env

Group sharing is manual-only after the 2026-05-25 reversal; there is no
IdP group sync. Remove the dead IDP_GROUP_SYNC_INTERVAL_MIN var and the
"LOCAL + IDP combined" wording so sample.env stops documenting a feature
that no longer exists.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* UN-2977 [FIX] Harden group-share signals + correct member_count and guards

Addresses self-review findings on PR #1986:
- member_count: count via a decoupled Subquery so the optional ?member
  filter on the same relation no longer collapses it to 1.
- Add a post_delete receiver purging ResourceGroupShare rows when a
  shareable resource is deleted (object_id is varchar, no FK/cascade).
- Wrap cleanup_user_org_access in transaction.atomic() with per-model
  error handling that logs and re-raises, so a partial purge can't
  re-open the rejoin backdoor.
- Skip-and-log malformed object_id UUIDs instead of 500ing the list.
- Error-handle the post-commit adapter default-cleanup save.
- Comment accuracy: shared_groups is polymorphic (not M2M); drop the
  duplicate service-account docstring bullet; clarify the mixin's
  writable vs read-only usage.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* UN-2977 [FIX] Restore close-on-success in share modals

This PR had moved the share-modal close into .finally() (close on
success or failure), discarding the user's selection on a 400/403.
Revert useShareModal, ListOfTools and Workflows to close only on
success — restoring pre-PR behavior and matching ToolSettings — so a
rejected share keeps the modal open for the user to review and retry.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* UN-2977 [FIX] Remove dead SharedGroupsSerializerMixin

All 7 resource serializers (6 OSS + cloud AgenticProject) declare
shared_groups as read_only, so DRF strips it from validated_data and the
mixin's create/update never wrote group shares — every group write flows
through ShareAuthorizationService._commit via the POST /share/ action.
The mixin was therefore dead code with a misleading docstring and a
latent org-scope footgun if a field were ever made writable without
re-adding validation. Delete it and unwire it from the 6 serializers;
the read_only shared_groups declarations stay.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* UN-2977 [FIX] Return 403 not 404 on shared-user mutation of tool config and LLM profile

DRF runs get_queryset() before has_object_permission(); when a sub-resource's
queryset filtered out rows reachable via parent-resource sharing, shared users
got HTTP 404 on PATCH/DELETE instead of the expected 403.

Strictly additive fix on read visibility (post-fix row set is a superset of
pre-fix for every caller class):

- permissions/permission.py: add IsParentWorkflowOwner — mirrors IsOwner but
  reads obj.workflow.created_by; admits org admin and service account.
- tool_instance_v2/views.py: broaden get_queryset to union prior per-creator
  rows with rows under workflows the user can access; gate update /
  partial_update / destroy on IsParentWorkflowOwner.
- prompt_studio/prompt_profile_manager_v2/models.py: extend
  ProfileManagerModelManager.for_user with a parent-CustomTool branch
  alongside the existing created_by | shared_users | shared_to_org branches.
  Viewset's IsOwner gate is unchanged.

The only mutate-side narrowing: a shared user can no longer mutate a
ToolInstance they themselves created under a non-owned workflow (was an
unintended path that let shared users inject + mutate tool config; matches
the user-stated requirement that shared users shouldn't change tool config).

Verified: pre-commit clean, identical mypy error counts vs HEAD on all three
files (no new errors introduced).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* UN-2977 [FIX] Consolidate shareable-resource registry and harden membership cleanup

Address review feedback on PR #1986:

- Add a single SHAREABLE_RESOURCES registry (tenant_account_v2/
  shareable_resources.py) and drive both the org-membership cleanup signals
  and the group blast-radius view from it, so the two lists can no longer
  drift. AgenticProject is now included, fixing the under-reported group
  delete count on cloud.
- Purge shared_users via the unscoped M2M through table scoped by the
  resource's organization, instead of the UserContext-org-scoped default
  manager which matched zero rows outside an HTTP request (e.g. tests /
  management commands), silently skipping the rejoin-backdoor cleanup.
- Tighten the adapter get_permissions comment and correct the pipeline
  shared_groups comment to match the other resource models.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* UN-2977 [FIX] Correct AgenticProject field names in shareable registry

The AgenticProject descriptor in SHAREABLE_RESOURCES hardcoded
name_field="project_name"/id_field="project_id", copied from the CustomTool
tool_id/tool_name pattern. The real cloud model (agentic_studio_v1.AgenticProject,
cloud PR #1508) uses ORM fields "id" (UUID PK) and "name"; project_id/
project_name exist only as dict keys in get_pipeline_status(). On cloud this made
the group blast-radius view run values_list("project_id","project_name") ->
FieldError/500. Pure OSS was unaffected (app absent, skipped via LookupError).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* UN-2977 [FIX] Make share-modal user revoke type-agnostic

handleDeleteUser compared a numeric item.id against selectedUsers, which
holds a mix of stringified (pre-loaded) and numeric (in-session) IDs, so
"Revoke Access" was a silent no-op for already-shared users. Normalize both
sides with String() so removal works regardless of ID type.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* UN-2977 [FIX] Address review: reorder authz, effective-member adapter cleanup, tenant hardening + tests

- Gate tool-instance reorder on workflow ownership; scope helper lookup
- Clear default adapter on effective-access loss (group/org), not just direct
- Harden group-share cleanup signal + skip models lacking shared_users
- Org-scope sharing read helpers + reject cross-org group writes
- Add registry drift system check; fix groups-service route; restore StrictMode
- useShareModal fallback + GroupMemberManager shape normalization
- Add group-sharing test suite (15 tests: matrix, signals, visibility, registry)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* UN-2977 [FIX] Replace hard-coded test password with generated secret (SonarCloud)

Resolves SonarCloud 'Credentials should not be hard-coded' on tests.py;
the literal is never used to authenticate. Flips New-Code Security Rating
to A and passes the Quality Gate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* UN-2977 [FIX] Preserve adapter owner's default on shared_to_org toggle-off

Effective-member diff in the adapter share action counted the owner as an
org member while shared_to_org=True; toggling it off dropped the owner into
the removed set and wiped their UserDefaultAdapter. The owner always retains
access via created_by, so exclude them from cleanup.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Rahul Johny <116638720+johnyrahul@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants