More explicit credential creation flow (#2363)

* more explcit drive credential creation flow

* remove logs

* update naming

* fix user-contributed formatting

* fix (^) v2
This commit is contained in:
pablodanswer
2024-09-08 12:09:23 -07:00
committed by GitHub
parent 80de408cef
commit 1555ac9dab
4 changed files with 71 additions and 69 deletions

View File

@@ -241,7 +241,8 @@ class NotionConnector(LoadConnector, PollConnector):
logger.warning( logger.warning(
f"Skipping 'external_object_instance_page' ('{result_block_id}') for base block '{base_block_id}': " f"Skipping 'external_object_instance_page' ('{result_block_id}') for base block '{base_block_id}': "
f"Notion API does not currently support reading external blocks (as of 24/07/03) " f"Notion API does not currently support reading external blocks (as of 24/07/03) "
f"(discussion: https://github.com/danswer-ai/danswer/issues/1761)") f"(discussion: https://github.com/danswer-ai/danswer/issues/1761)"
)
continue continue
cur_result_text_arr = [] cur_result_text_arr = []

View File

@@ -4,6 +4,7 @@ from uuid import UUID
from pydantic import BaseModel from pydantic import BaseModel
from pydantic import Field from pydantic import Field
from pydantic import model_validator
from danswer.configs.app_configs import MASK_CREDENTIAL_PREFIX from danswer.configs.app_configs import MASK_CREDENTIAL_PREFIX
from danswer.configs.constants import DocumentSource from danswer.configs.constants import DocumentSource
@@ -346,8 +347,18 @@ class GoogleServiceAccountKey(BaseModel):
class GoogleServiceAccountCredentialRequest(BaseModel): class GoogleServiceAccountCredentialRequest(BaseModel):
google_drive_delegated_user: str | None # email of user to impersonate google_drive_delegated_user: str | None = None # email of user to impersonate
gmail_delegated_user: str | None # email of user to impersonate gmail_delegated_user: str | None = None # email of user to impersonate
@model_validator(mode="after")
def check_user_delegation(self) -> "GoogleServiceAccountCredentialRequest":
if (self.google_drive_delegated_user is None) == (
self.gmail_delegated_user is None
):
raise ValueError(
"Exactly one of google_drive_delegated_user or gmail_delegated_user must be set"
)
return self
class FileUploadResponse(BaseModel): class FileUploadResponse(BaseModel):

View File

@@ -17,6 +17,8 @@ import {
GoogleDriveServiceAccountCredentialJson, GoogleDriveServiceAccountCredentialJson,
} from "@/lib/connectors/credentials"; } from "@/lib/connectors/credentials";
import { Button as TremorButton } from "@tremor/react";
type GoogleDriveCredentialJsonTypes = "authorized_user" | "service_account"; type GoogleDriveCredentialJsonTypes = "authorized_user" | "service_account";
export const DriveJsonUpload = ({ export const DriveJsonUpload = ({
@@ -344,7 +346,7 @@ export const DriveOAuthSection = ({
if (serviceAccountKeyData?.service_account_email) { if (serviceAccountKeyData?.service_account_email) {
return ( return (
<div> <div>
<p className="text-sm mb-2"> <p className="text-sm mb-6">
When using a Google Drive Service Account, you can either have Danswer When using a Google Drive Service Account, you can either have Danswer
act as the service account itself OR you can specify an account for act as the service account itself OR you can specify an account for
the service account to impersonate. the service account to impersonate.
@@ -356,70 +358,59 @@ export const DriveOAuthSection = ({
the documents you want to index with the service account. the documents you want to index with the service account.
</p> </p>
<Card> <Formik
<Formik initialValues={{
initialValues={{ google_drive_delegated_user: "",
google_drive_delegated_user: "", }}
}} validationSchema={Yup.object().shape({
validationSchema={Yup.object().shape({ google_drive_delegated_user: Yup.string().optional(),
google_drive_delegated_user: Yup.string().optional(), })}
})} onSubmit={async (values, formikHelpers) => {
onSubmit={async (values, formikHelpers) => { formikHelpers.setSubmitting(true);
formikHelpers.setSubmitting(true); const response = await fetch(
"/api/manage/admin/connector/google-drive/service-account-credential",
const response = await fetch( {
"/api/manage/admin/connector/google-drive/service-account-credential", method: "PUT",
{ headers: {
method: "PUT", "Content-Type": "application/json",
headers: { },
"Content-Type": "application/json", body: JSON.stringify({
}, google_drive_delegated_user:
body: JSON.stringify({ values.google_drive_delegated_user,
google_drive_delegated_user: }),
values.google_drive_delegated_user,
}),
}
);
if (response.ok) {
setPopup({
message: "Successfully created service account credential",
type: "success",
});
} else {
const errorMsg = await response.text();
setPopup({
message: `Failed to create service account credential - ${errorMsg}`,
type: "error",
});
} }
refreshCredentials(); );
}}
> if (response.ok) {
{({ isSubmitting }) => ( setPopup({
<Form> message: "Successfully created service account credential",
<TextFormField type: "success",
name="google_drive_delegated_user" });
label="[Optional] User email to impersonate:" } else {
subtext="If left blank, Danswer will use the service account itself." const errorMsg = await response.text();
/> setPopup({
<div className="flex"> message: `Failed to create service account credential - ${errorMsg}`,
<button type: "error",
type="submit" });
disabled={isSubmitting} }
className={ refreshCredentials();
"bg-slate-500 hover:bg-slate-700 text-white " + }}
"font-bold py-2 px-4 rounded focus:outline-none " + >
"focus:shadow-outline w-full max-w-sm mx-auto" {({ isSubmitting }) => (
} <Form>
> <TextFormField
Submit name="google_drive_delegated_user"
</button> label="[Optional] User email to impersonate:"
</div> subtext="If left blank, Danswer will use the service account itself."
</Form> />
)} <div className="flex">
</Formik> <TremorButton type="submit" disabled={isSubmitting}>
</Card> Create Credential
</TremorButton>
</div>
</Form>
)}
</Formik>
</div> </div>
); );
} }

View File

@@ -8,8 +8,6 @@ import { ErrorCallout } from "@/components/ErrorCallout";
import { LoadingAnimation } from "@/components/Loading"; import { LoadingAnimation } from "@/components/Loading";
import { usePopup } from "@/components/admin/connectors/Popup"; import { usePopup } from "@/components/admin/connectors/Popup";
import { ConnectorIndexingStatus } from "@/lib/types"; import { ConnectorIndexingStatus } from "@/lib/types";
import { getCurrentUser } from "@/lib/user";
import { User, UserRole } from "@/lib/types";
import { usePublicCredentials } from "@/lib/hooks"; import { usePublicCredentials } from "@/lib/hooks";
import { Title } from "@tremor/react"; import { Title } from "@tremor/react";
import { DriveJsonUploadSection, DriveOAuthSection } from "./Credential"; import { DriveJsonUploadSection, DriveOAuthSection } from "./Credential";
@@ -109,6 +107,7 @@ const GDriveMain = ({}: {}) => {
| undefined = credentialsData.find( | undefined = credentialsData.find(
(credential) => credential.credential_json?.google_drive_service_account_key (credential) => credential.credential_json?.google_drive_service_account_key
); );
const googleDriveConnectorIndexingStatuses: ConnectorIndexingStatus< const googleDriveConnectorIndexingStatuses: ConnectorIndexingStatus<
GoogleDriveConfig, GoogleDriveConfig,
GoogleDriveCredentialJson GoogleDriveCredentialJson