262 lines
8.8 KiB
Python
262 lines
8.8 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
from fastapi import APIRouter, Depends
|
|
from sqlalchemy.orm import Session
|
|
|
|
from govoplan_core.auth import ApiPrincipal, require_any_scope
|
|
from govoplan_files.backend.change_tracking import (
|
|
FILES_CONNECTOR_PROFILES_COLLECTION,
|
|
)
|
|
from govoplan_files.backend.schemas import (
|
|
FileConnectorProfileResponse,
|
|
FileConnectorProfileUpdateRequest,
|
|
)
|
|
from govoplan_core.db.session import get_session
|
|
from govoplan_files.backend.storage.common import FileStorageError
|
|
from govoplan_files.backend.storage.connector_credential_deletion import (
|
|
delete_connector_profile_row,
|
|
)
|
|
from govoplan_files.backend.storage.connector_deployment import (
|
|
connector_effective_endpoint_url,
|
|
)
|
|
from govoplan_files.backend.storage.connector_profile_store import (
|
|
connector_profile_from_row,
|
|
get_connector_profile_row,
|
|
update_connector_profile_row,
|
|
)
|
|
from govoplan_files.backend.storage.connector_policy import (
|
|
ConnectorPolicyDenied,
|
|
)
|
|
|
|
|
|
from govoplan_files.backend.route_support import (
|
|
FILES_CONNECTOR_PROFILE_RESOURCE,
|
|
_can_read_disabled_connector_profiles,
|
|
_connector_policy_error,
|
|
_credential_row_for_profile,
|
|
_ensure_connector_configuration_allowed,
|
|
_ensure_connector_local_policy_allowed,
|
|
_http_error,
|
|
_record_connector_settings_change,
|
|
_require_connector_profile_write,
|
|
_visible_connector_profile,
|
|
)
|
|
|
|
router = APIRouter(prefix="/files", tags=["files"])
|
|
|
|
|
|
@router.get(
|
|
"/connectors/profiles/{profile_id}", response_model=FileConnectorProfileResponse
|
|
)
|
|
def get_connector_profile(
|
|
profile_id: str,
|
|
campaign_id: str | None = None,
|
|
include_disabled: bool = False,
|
|
session: Session = Depends(get_session),
|
|
principal: ApiPrincipal = Depends(
|
|
require_any_scope(
|
|
"files:file:read",
|
|
"files:file:upload",
|
|
"files:file:download",
|
|
"files:file:admin",
|
|
"system:settings:read",
|
|
"admin:settings:read",
|
|
)
|
|
),
|
|
):
|
|
try:
|
|
profile = _visible_connector_profile(
|
|
session,
|
|
principal,
|
|
profile_id,
|
|
campaign_id=campaign_id,
|
|
include_disabled=include_disabled
|
|
and _can_read_disabled_connector_profiles(principal),
|
|
include_effective_policy=False,
|
|
)
|
|
except (OSError, ValueError, json.JSONDecodeError) as exc:
|
|
raise _http_error(exc) from exc
|
|
return FileConnectorProfileResponse(**profile.to_response())
|
|
|
|
|
|
@router.patch(
|
|
"/connectors/profiles/{profile_id}", response_model=FileConnectorProfileResponse
|
|
)
|
|
def update_connector_profile(
|
|
profile_id: str,
|
|
payload: FileConnectorProfileUpdateRequest,
|
|
session: Session = Depends(get_session),
|
|
principal: ApiPrincipal = Depends(
|
|
require_any_scope(
|
|
"files:file:admin", "system:settings:write", "admin:settings:write"
|
|
)
|
|
),
|
|
):
|
|
try:
|
|
row = get_connector_profile_row(
|
|
session,
|
|
tenant_id=principal.tenant_id,
|
|
profile_id=profile_id,
|
|
include_disabled=True,
|
|
)
|
|
except FileStorageError as exc:
|
|
raise _http_error(exc, not_found=True) from exc
|
|
_require_connector_profile_write(principal, row.scope_type)
|
|
credentials = payload.credentials
|
|
try:
|
|
credential_profile_id = (
|
|
payload.credential_profile_id
|
|
if payload.credential_profile_id is not None
|
|
else row.credential_profile_id
|
|
)
|
|
provider = payload.provider if payload.provider is not None else row.provider
|
|
credential_row = _credential_row_for_profile(
|
|
session,
|
|
principal,
|
|
credential_profile_id=credential_profile_id,
|
|
provider=provider,
|
|
profile_id=row.id,
|
|
scope_type=row.scope_type,
|
|
scope_id=row.scope_id,
|
|
include_disabled=True,
|
|
)
|
|
_ensure_connector_configuration_allowed(
|
|
session,
|
|
principal,
|
|
connector_id=row.id,
|
|
credential_id=credential_profile_id,
|
|
provider=provider,
|
|
endpoint_url=connector_effective_endpoint_url(
|
|
provider=provider,
|
|
endpoint_url=payload.endpoint_url
|
|
if payload.endpoint_url is not None
|
|
else row.endpoint_url,
|
|
metadata=payload.metadata
|
|
if payload.metadata is not None
|
|
else row.metadata_,
|
|
),
|
|
base_path=payload.base_path
|
|
if payload.base_path is not None
|
|
else row.base_path,
|
|
scope_type=row.scope_type,
|
|
scope_id=row.scope_id,
|
|
operation="configure",
|
|
)
|
|
if payload.policy is not None:
|
|
_ensure_connector_local_policy_allowed(
|
|
session,
|
|
principal,
|
|
scope_type=row.scope_type,
|
|
scope_id=row.scope_id,
|
|
policy=payload.policy,
|
|
)
|
|
update_connector_profile_row(
|
|
session,
|
|
row,
|
|
user_id=principal.user.id,
|
|
label=payload.label,
|
|
provider=payload.provider,
|
|
endpoint_url=payload.endpoint_url,
|
|
base_path=payload.base_path,
|
|
enabled=payload.enabled,
|
|
credential_profile_id=payload.credential_profile_id,
|
|
credential_mode=payload.credential_mode,
|
|
username=credentials.username if credentials else None,
|
|
password=credentials.password if credentials else None,
|
|
token=credentials.token if credentials else None,
|
|
password_env=credentials.password_env if credentials else None,
|
|
token_env=credentials.token_env if credentials else None,
|
|
secret_ref=credentials.secret_ref if credentials else None,
|
|
clear_password=payload.clear_password,
|
|
clear_token=payload.clear_token,
|
|
capabilities=payload.capabilities,
|
|
policy=payload.policy,
|
|
metadata=payload.metadata,
|
|
)
|
|
_record_connector_settings_change(
|
|
session,
|
|
collection=FILES_CONNECTOR_PROFILES_COLLECTION,
|
|
resource_type=FILES_CONNECTOR_PROFILE_RESOURCE,
|
|
resource_id=row.id,
|
|
operation="updated",
|
|
principal=principal,
|
|
tenant_id=row.tenant_id,
|
|
payload={
|
|
"scope_type": row.scope_type,
|
|
"scope_id": row.scope_id,
|
|
"provider": row.provider,
|
|
},
|
|
)
|
|
session.commit()
|
|
session.refresh(row)
|
|
return FileConnectorProfileResponse(
|
|
**connector_profile_from_row(
|
|
row, credential_row=credential_row
|
|
).to_response()
|
|
)
|
|
except ConnectorPolicyDenied as exc:
|
|
session.rollback()
|
|
raise _connector_policy_error(exc) from exc
|
|
except (FileStorageError, ValueError, json.JSONDecodeError) as exc:
|
|
session.rollback()
|
|
raise _http_error(exc) from exc
|
|
|
|
|
|
@router.delete(
|
|
"/connectors/profiles/{profile_id}", response_model=FileConnectorProfileResponse
|
|
)
|
|
def deactivate_connector_profile(
|
|
profile_id: str,
|
|
session: Session = Depends(get_session),
|
|
principal: ApiPrincipal = Depends(
|
|
require_any_scope(
|
|
"files:file:admin", "system:settings:write", "admin:settings:write"
|
|
)
|
|
),
|
|
):
|
|
try:
|
|
row = get_connector_profile_row(
|
|
session,
|
|
tenant_id=principal.tenant_id,
|
|
profile_id=profile_id,
|
|
include_disabled=True,
|
|
)
|
|
except FileStorageError as exc:
|
|
raise _http_error(exc, not_found=True) from exc
|
|
_require_connector_profile_write(principal, row.scope_type)
|
|
try:
|
|
changed = delete_connector_profile_row(
|
|
session,
|
|
row,
|
|
deletion_reason="api_delete",
|
|
user_id=principal.user.id,
|
|
api_key_id=principal.api_key.id if principal.api_key else None,
|
|
)
|
|
if changed:
|
|
_record_connector_settings_change(
|
|
session,
|
|
collection=FILES_CONNECTOR_PROFILES_COLLECTION,
|
|
resource_type=FILES_CONNECTOR_PROFILE_RESOURCE,
|
|
resource_id=row.id,
|
|
operation="deleted",
|
|
principal=principal,
|
|
tenant_id=row.tenant_id,
|
|
payload={
|
|
"scope_type": row.scope_type,
|
|
"scope_id": row.scope_id,
|
|
"provider": row.provider,
|
|
},
|
|
)
|
|
session.commit()
|
|
session.refresh(row)
|
|
return FileConnectorProfileResponse(
|
|
**connector_profile_from_row(row).to_response()
|
|
)
|
|
except FileStorageError as exc:
|
|
session.rollback()
|
|
raise _http_error(exc) from exc
|
|
except Exception:
|
|
session.rollback()
|
|
raise
|