feat(connectors): set up Gmail accounts in the connector form (#15995)

This commit is contained in:
Raunak Bhagat authored and GitHub committed 2026-10-14 22:25:39 +00:00
1 parent c52675c30f
commit e6392cd30c
29 files changed
+322 -1087

No files matched your search

@@ -37,6 +37,9 @@ from onyx.error_handling.exceptions import OnyxError
from onyx.server.documents.capability_check_runs import (
start_capability_checks_for_new_credential,
)
from onyx.server.documents.draft_credentials import (
validate_google_service_account_key,
)
from onyx.server.documents.models import (
CredentialBase,
CredentialDataUpdateRequest,
@@ -311,6 +314,14 @@ def create_credential_from_model(
db_session: Session = Depends(get_session),
) -> ObjectCreationIdResponse:
assert_credential_share_within_scope(credential_info, user, db_session)
# A typed Google account can come here (the account dialog) as well as
# through a draft, so its key is checked the same way.
try:
validate_google_service_account_key(
credential_info.source, credential_info.credential_json
)
except ValueError as e:
raise OnyxError(OnyxErrorCode.INVALID_INPUT, str(e)) from e
credential = create_credential(credential_info, user, db_session)
emit_audit_event(
@@ -1,14 +1,35 @@
"""A credential created for one source of a family is offered to the family's
other sources, shown in their keys."""
import json
from typing import Any
import pytest
from onyx.configs.constants import DocumentSource
from onyx.error_handling.error_codes import OnyxErrorCode
from tests.integration.common_utils.constants import API_SERVER_URL
from tests.integration.common_utils.http_client import client
from tests.integration.common_utils.managers.credential import CredentialManager
from tests.integration.common_utils.test_models import DATestUser
# Saving a Google credential checks that its key is a service-account key.
_SERVICE_ACCOUNT_KEY = json.dumps(
{
"type": "service_account",
"project_id": "project",
"private_key_id": "key-id",
"private_key": "-----BEGIN PRIVATE KEY-----\nkey\n-----END PRIVATE KEY-----\n",
"client_email": "indexer@project.iam.gserviceaccount.com",
"client_id": "123",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/indexer",
"universe_domain": "googleapis.com",
}
)
def _similar_credentials(
source: DocumentSource, user: DATestUser
@@ -57,7 +78,7 @@ def test_gmail_credential_is_listed_for_google_drive(admin_user: DATestUser) ->
user_performing_action=admin_user,
source=DocumentSource.GMAIL,
credential_json={
"google_service_account_key": '{"type": "service_account"}',
"google_service_account_key": _SERVICE_ACCOUNT_KEY,
"google_primary_admin": "admin@example.com",
"authentication_method": "uploaded",
},
@@ -78,6 +99,42 @@ def test_gmail_credential_is_listed_for_google_drive(admin_user: DATestUser) ->
}
@pytest.mark.parametrize(
"service_account_key",
[
"not json",
json.dumps({"web": {"client_id": "oauth-app"}}),
json.dumps({"type": "service_account"}),
json.dumps({**json.loads(_SERVICE_ACCOUNT_KEY), "type": "authorized_user"}),
],
ids=["malformed", "oauth_app", "incomplete", "wrong_type"],
)
def test_google_credential_rejects_a_bad_service_account_key(
admin_user: DATestUser, service_account_key: str
) -> None:
# Under test.
response = client.post(
f"{API_SERVER_URL}/manage/credential",
json={
"credential_json": {
"google_service_account_key": service_account_key,
"google_primary_admin": "admin@example.com",
"authentication_method": "uploaded",
},
"admin_public": True,
"source": DocumentSource.GMAIL.value,
"name": None,
"curator_public": False,
"groups": [],
},
headers=admin_user.headers,
)
# Postcondition.
assert response.status_code == OnyxErrorCode.INVALID_INPUT.status_code
assert response.json()["error_code"] == OnyxErrorCode.INVALID_INPUT.code
def test_sharepoint_credential_is_listed_for_teams(admin_user: DATestUser) -> None:
# Precondition.
sharepoint_credential = CredentialManager.create(