@opengeni/db 0.36.1 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/canonical-human-identities.js +3 -3
- package/dist/{chunk-HSOWSYOX.js → chunk-4SR4HKKA.js} +2 -2
- package/dist/{chunk-5KTIEDX7.js → chunk-6ZYKCRFO.js} +124 -16
- package/dist/chunk-6ZYKCRFO.js.map +1 -0
- package/dist/{chunk-VTPRIXKG.js → chunk-CUPM3FGB.js} +3 -3
- package/dist/{chunk-GYO7ED2Q.js → chunk-LTG3S6ZY.js} +2 -2
- package/dist/{chunk-VKLB2ZGI.js → chunk-NXAFLCQX.js} +81 -2
- package/dist/chunk-NXAFLCQX.js.map +1 -0
- package/dist/{chunk-FPVAD4CO.js → chunk-WD5ROSLL.js} +2 -2
- package/dist/{chunk-Z7VYMNPZ.js → chunk-ZWREYEWA.js} +2 -2
- package/dist/editable-artifact-durable-export.d.ts +10 -0
- package/dist/editable-artifact-durable-export.js +18 -3
- package/dist/editable-artifact-durable-export.js.map +1 -1
- package/dist/editable-artifacts.js +3 -3
- package/dist/index.d.ts +40 -8
- package/dist/index.js +509 -59
- package/dist/index.js.map +1 -1
- package/dist/knowledge-source-sync-schema.d.ts +730 -0
- package/dist/knowledge-source-sync.d.ts +60 -0
- package/dist/provision-roles.js +1 -1
- package/dist/runtime-posture.d.ts +3 -3
- package/dist/schema.d.ts +21 -4
- package/dist/schema.js +5 -1
- package/dist/session-queue-commands.d.ts +1 -1
- package/dist/session-realtime-ledger.d.ts +1 -0
- package/dist/session-tenancy.js +3 -3
- package/dist/video-generation.js +3 -3
- package/drizzle/0240_model_context_user_messages.sql +204 -0
- package/drizzle/0243_google_drive_object_acl_authority.sql +554 -0
- package/package.json +4 -4
- package/src/editable-artifact-durable-export.ts +27 -0
- package/src/index.ts +212 -13
- package/src/knowledge-source-sync-schema.ts +83 -0
- package/src/knowledge-source-sync.ts +530 -0
- package/src/provision-roles.ts +36 -0
- package/src/runtime-posture.ts +49 -1
- package/src/schema.ts +30 -7
- package/src/session-queue-commands.ts +13 -14
- package/src/session-realtime-context.ts +16 -0
- package/src/session-realtime-ledger.ts +53 -1
- package/dist/chunk-5KTIEDX7.js.map +0 -1
- package/dist/chunk-VKLB2ZGI.js.map +0 -1
- /package/dist/{chunk-HSOWSYOX.js.map → chunk-4SR4HKKA.js.map} +0 -0
- /package/dist/{chunk-VTPRIXKG.js.map → chunk-CUPM3FGB.js.map} +0 -0
- /package/dist/{chunk-GYO7ED2Q.js.map → chunk-LTG3S6ZY.js.map} +0 -0
- /package/dist/{chunk-FPVAD4CO.js.map → chunk-WD5ROSLL.js.map} +0 -0
- /package/dist/{chunk-Z7VYMNPZ.js.map → chunk-ZWREYEWA.js.map} +0 -0
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
KnowledgeSourceSyncRunSummary,
|
|
5
5
|
type ScheduledTask,
|
|
6
6
|
} from "@opengeni/contracts";
|
|
7
|
+
import { createHash } from "node:crypto";
|
|
7
8
|
import { and, eq, inArray, sql } from "drizzle-orm";
|
|
8
9
|
import type { Database } from "./database";
|
|
9
10
|
import { setSubjectRlsContext, withRlsContext } from "./database";
|
|
@@ -43,6 +44,40 @@ export type KnowledgeSourceSyncObservationFence = {
|
|
|
43
44
|
observations: KnowledgeSourceSyncObservationFloor[];
|
|
44
45
|
};
|
|
45
46
|
|
|
47
|
+
export type GoogleDriveObjectAclPrincipal = {
|
|
48
|
+
type: "user" | "group" | "domain" | "anyone";
|
|
49
|
+
permissionId?: string | null;
|
|
50
|
+
emailAddress?: string | null;
|
|
51
|
+
domain?: string | null;
|
|
52
|
+
role: "owner" | "organizer" | "fileOrganizer" | "writer" | "commenter" | "reader";
|
|
53
|
+
inherited?: boolean;
|
|
54
|
+
allowFileDiscovery?: boolean | null;
|
|
55
|
+
expirationTime?: string | null;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export type GoogleDriveObjectAclEvidenceInput = {
|
|
59
|
+
accountId: string;
|
|
60
|
+
workspaceId: string;
|
|
61
|
+
obligationId: string;
|
|
62
|
+
connectionId: string;
|
|
63
|
+
connectionVersion: number;
|
|
64
|
+
sourceGooglePermissionId: string;
|
|
65
|
+
sourceSyncGeneration: number;
|
|
66
|
+
sourceConfigGeneration: number;
|
|
67
|
+
sourceLifecycleGeneration: number;
|
|
68
|
+
objectLifecycleGeneration: number;
|
|
69
|
+
objectVersionGeneration: number;
|
|
70
|
+
providerRevision: string | null;
|
|
71
|
+
driveId: string | null;
|
|
72
|
+
aclRevision: string;
|
|
73
|
+
eligibility: "eligible" | "denied";
|
|
74
|
+
observedAt: string;
|
|
75
|
+
expiresAt: string;
|
|
76
|
+
citationLocator: Record<string, unknown>;
|
|
77
|
+
operationId: string;
|
|
78
|
+
principals: GoogleDriveObjectAclPrincipal[];
|
|
79
|
+
};
|
|
80
|
+
|
|
46
81
|
export type KnowledgeSourceSyncObjectObservationResult = KnowledgeSourceSyncObservationFloor & {
|
|
47
82
|
disposition: "accepted" | "replayed" | "stale" | "conflict";
|
|
48
83
|
currentVersion: {
|
|
@@ -1614,6 +1649,501 @@ export async function recordKnowledgeSourceSyncAclEvidence(
|
|
|
1614
1649
|
);
|
|
1615
1650
|
}
|
|
1616
1651
|
|
|
1652
|
+
/**
|
|
1653
|
+
* Persist one append-only Google Drive object ACL observation and atomically
|
|
1654
|
+
* make it the current authorization pointer for the exact index obligation.
|
|
1655
|
+
* Raw provider principals never enter storage: only domain-separated hashes
|
|
1656
|
+
* are retained. Denial deletes chunks in the same transaction so stale vector
|
|
1657
|
+
* or keyword rows cannot survive permission loss.
|
|
1658
|
+
*/
|
|
1659
|
+
export async function recordGoogleDriveObjectAclEvidence(
|
|
1660
|
+
db: Database,
|
|
1661
|
+
input: GoogleDriveObjectAclEvidenceInput,
|
|
1662
|
+
): Promise<{ evidenceId: string; aclHash: string }> {
|
|
1663
|
+
const observedAt = exactDate(input.observedAt, "Google Drive ACL observedAt");
|
|
1664
|
+
const expiresAt = exactDate(input.expiresAt, "Google Drive ACL expiresAt");
|
|
1665
|
+
if (expiresAt.getTime() <= observedAt.getTime()) {
|
|
1666
|
+
throw new Error("Google Drive ACL evidence must expire after it was observed");
|
|
1667
|
+
}
|
|
1668
|
+
if (input.principals.length > 1_000) {
|
|
1669
|
+
throw new Error("Google Drive ACL evidence exceeds 1000 principals");
|
|
1670
|
+
}
|
|
1671
|
+
const principals = input.principals.map(normalizeGoogleDriveAclPrincipal);
|
|
1672
|
+
const canonicalAcl = principals
|
|
1673
|
+
.map((principal) => JSON.stringify(principal))
|
|
1674
|
+
.sort((left, right) => left.localeCompare(right));
|
|
1675
|
+
const aclHash = sha256Hex(JSON.stringify(canonicalAcl));
|
|
1676
|
+
const sourcePrincipalHash = principalHash("permission", input.sourceGooglePermissionId);
|
|
1677
|
+
const inputHash = sha256Hex(
|
|
1678
|
+
JSON.stringify({
|
|
1679
|
+
version: 1,
|
|
1680
|
+
obligationId: input.obligationId,
|
|
1681
|
+
connectionId: input.connectionId,
|
|
1682
|
+
connectionVersion: input.connectionVersion,
|
|
1683
|
+
sourcePrincipalHash,
|
|
1684
|
+
sourceSyncGeneration: input.sourceSyncGeneration,
|
|
1685
|
+
sourceConfigGeneration: input.sourceConfigGeneration,
|
|
1686
|
+
sourceLifecycleGeneration: input.sourceLifecycleGeneration,
|
|
1687
|
+
objectLifecycleGeneration: input.objectLifecycleGeneration,
|
|
1688
|
+
objectVersionGeneration: input.objectVersionGeneration,
|
|
1689
|
+
providerRevision: input.providerRevision,
|
|
1690
|
+
driveId: input.driveId,
|
|
1691
|
+
aclRevision: input.aclRevision,
|
|
1692
|
+
aclHash,
|
|
1693
|
+
eligibility: input.eligibility,
|
|
1694
|
+
observedAt: observedAt.toISOString(),
|
|
1695
|
+
expiresAt: expiresAt.toISOString(),
|
|
1696
|
+
citationLocator: input.citationLocator,
|
|
1697
|
+
}),
|
|
1698
|
+
);
|
|
1699
|
+
|
|
1700
|
+
return await withRlsContext(
|
|
1701
|
+
db,
|
|
1702
|
+
{ accountId: input.accountId, workspaceId: input.workspaceId },
|
|
1703
|
+
async (scopedDb) =>
|
|
1704
|
+
await scopedDb.transaction(async (tx) => {
|
|
1705
|
+
const [observedObligation] = await tx
|
|
1706
|
+
.select()
|
|
1707
|
+
.from(schema.knowledgeSourceSyncIndexObligations)
|
|
1708
|
+
.where(eq(schema.knowledgeSourceSyncIndexObligations.id, input.obligationId))
|
|
1709
|
+
.limit(1);
|
|
1710
|
+
if (!observedObligation) {
|
|
1711
|
+
throw new Error("Google Drive ACL evidence obligation was not found");
|
|
1712
|
+
}
|
|
1713
|
+
await setSubjectRlsContext(tx, observedObligation.initiatingSubjectId);
|
|
1714
|
+
const [state] = await tx
|
|
1715
|
+
.select()
|
|
1716
|
+
.from(schema.knowledgeSourceSyncStates)
|
|
1717
|
+
.where(eq(schema.knowledgeSourceSyncStates.sourceId, observedObligation.sourceId))
|
|
1718
|
+
.for("update")
|
|
1719
|
+
.limit(1);
|
|
1720
|
+
if (!state) throw new Error("Google Drive sync authorization is no longer active");
|
|
1721
|
+
await tx.execute(sql`
|
|
1722
|
+
SELECT knowledge_source_sync_lock_authority(
|
|
1723
|
+
${input.accountId}::uuid,
|
|
1724
|
+
${observedObligation.sourceId}::uuid,
|
|
1725
|
+
${observedObligation.knowledgeSourceObjectId}::uuid
|
|
1726
|
+
)
|
|
1727
|
+
`);
|
|
1728
|
+
const [[source], [object], [version], [obligation], [connection]] = await Promise.all([
|
|
1729
|
+
tx
|
|
1730
|
+
.select()
|
|
1731
|
+
.from(schema.knowledgeSources)
|
|
1732
|
+
.where(eq(schema.knowledgeSources.id, observedObligation.sourceId))
|
|
1733
|
+
.limit(1),
|
|
1734
|
+
tx
|
|
1735
|
+
.select()
|
|
1736
|
+
.from(schema.knowledgeSourceObjects)
|
|
1737
|
+
.where(eq(schema.knowledgeSourceObjects.id, observedObligation.knowledgeSourceObjectId))
|
|
1738
|
+
.limit(1),
|
|
1739
|
+
tx
|
|
1740
|
+
.select()
|
|
1741
|
+
.from(schema.knowledgeDocumentVersions)
|
|
1742
|
+
.where(
|
|
1743
|
+
eq(
|
|
1744
|
+
schema.knowledgeDocumentVersions.id,
|
|
1745
|
+
observedObligation.knowledgeDocumentVersionId,
|
|
1746
|
+
),
|
|
1747
|
+
)
|
|
1748
|
+
.limit(1),
|
|
1749
|
+
tx
|
|
1750
|
+
.select()
|
|
1751
|
+
.from(schema.knowledgeSourceSyncIndexObligations)
|
|
1752
|
+
.where(eq(schema.knowledgeSourceSyncIndexObligations.id, input.obligationId))
|
|
1753
|
+
.for("update")
|
|
1754
|
+
.limit(1),
|
|
1755
|
+
tx
|
|
1756
|
+
.select()
|
|
1757
|
+
.from(schema.connections)
|
|
1758
|
+
.where(eq(schema.connections.id, input.connectionId))
|
|
1759
|
+
.limit(1),
|
|
1760
|
+
]);
|
|
1761
|
+
const [provider] = source
|
|
1762
|
+
? await tx
|
|
1763
|
+
.select()
|
|
1764
|
+
.from(schema.knowledgeProviders)
|
|
1765
|
+
.where(eq(schema.knowledgeProviders.id, source.providerId))
|
|
1766
|
+
.limit(1)
|
|
1767
|
+
: [];
|
|
1768
|
+
const [document] = obligation
|
|
1769
|
+
? await tx
|
|
1770
|
+
.select()
|
|
1771
|
+
.from(schema.documents)
|
|
1772
|
+
.where(eq(schema.documents.id, obligation.documentId))
|
|
1773
|
+
.for("update")
|
|
1774
|
+
.limit(1)
|
|
1775
|
+
: [];
|
|
1776
|
+
const metadata = connection?.metadata ?? {};
|
|
1777
|
+
const connectionPermissionId = cleanBoundedString(metadata.googlePermissionId, 256);
|
|
1778
|
+
const connectionLifecycle =
|
|
1779
|
+
metadata.lifecycle && typeof metadata.lifecycle === "object"
|
|
1780
|
+
? cleanBoundedString((metadata.lifecycle as Record<string, unknown>).state, 64)
|
|
1781
|
+
: null;
|
|
1782
|
+
const hasDriveReadScope =
|
|
1783
|
+
Array.isArray(connection?.grantedScopes) &&
|
|
1784
|
+
connection.grantedScopes.some(
|
|
1785
|
+
(scope) =>
|
|
1786
|
+
scope === "https://www.googleapis.com/auth/drive" ||
|
|
1787
|
+
scope === "https://www.googleapis.com/auth/drive.readonly",
|
|
1788
|
+
);
|
|
1789
|
+
if (
|
|
1790
|
+
!obligation ||
|
|
1791
|
+
!source ||
|
|
1792
|
+
!object ||
|
|
1793
|
+
!version ||
|
|
1794
|
+
!provider ||
|
|
1795
|
+
!document ||
|
|
1796
|
+
!connection ||
|
|
1797
|
+
provider.providerKey !== "google-drive" ||
|
|
1798
|
+
provider.lifecycleState !== "active" ||
|
|
1799
|
+
state.connectionId !== input.connectionId ||
|
|
1800
|
+
state.connectionProviderDomain !== "googleapis.com" ||
|
|
1801
|
+
state.connectionKind !== "oauth2" ||
|
|
1802
|
+
state.connectionOwnerSubjectId !== connection.subjectId ||
|
|
1803
|
+
state.reconnectRequired ||
|
|
1804
|
+
connection.accountId !== input.accountId ||
|
|
1805
|
+
connection.subjectId !== observedObligation.initiatingSubjectId ||
|
|
1806
|
+
connection.providerDomain !== "googleapis.com" ||
|
|
1807
|
+
connection.kind !== "oauth2" ||
|
|
1808
|
+
connection.status !== "active" ||
|
|
1809
|
+
connection.version !== input.connectionVersion ||
|
|
1810
|
+
connection.version < state.connectionVersion ||
|
|
1811
|
+
metadata.accessMode !== "readonly" ||
|
|
1812
|
+
(connectionLifecycle !== null && connectionLifecycle !== "active") ||
|
|
1813
|
+
!hasDriveReadScope ||
|
|
1814
|
+
!connectionPermissionId ||
|
|
1815
|
+
principalHash("permission", connectionPermissionId) !== sourcePrincipalHash ||
|
|
1816
|
+
input.sourceSyncGeneration !== obligation.sourceSyncGeneration ||
|
|
1817
|
+
state.sourceSyncGeneration < obligation.sourceSyncGeneration ||
|
|
1818
|
+
state.sourceConfigGeneration !== input.sourceConfigGeneration ||
|
|
1819
|
+
state.sourceConfigGeneration !== obligation.sourceConfigGeneration ||
|
|
1820
|
+
state.sourceLifecycleGeneration !== input.sourceLifecycleGeneration ||
|
|
1821
|
+
state.sourceLifecycleGeneration !== obligation.sourceLifecycleGeneration ||
|
|
1822
|
+
source.lifecycleState !== "active" ||
|
|
1823
|
+
source.syncGeneration < obligation.sourceSyncGeneration ||
|
|
1824
|
+
source.lifecycleGeneration !== obligation.sourceLifecycleGeneration ||
|
|
1825
|
+
source.currentAclGeneration !== version.aclGeneration ||
|
|
1826
|
+
object.sourceId !== obligation.sourceId ||
|
|
1827
|
+
object.externalObjectId !== obligation.externalObjectId ||
|
|
1828
|
+
object.lifecycleState !== "active" ||
|
|
1829
|
+
object.lifecycleGeneration !== obligation.objectLifecycleGeneration ||
|
|
1830
|
+
object.versionGeneration !== obligation.objectVersionGeneration ||
|
|
1831
|
+
object.currentVersionId !== obligation.knowledgeDocumentVersionId ||
|
|
1832
|
+
version.sourceId !== obligation.sourceId ||
|
|
1833
|
+
version.objectId !== obligation.knowledgeSourceObjectId ||
|
|
1834
|
+
version.versionGeneration !== obligation.objectVersionGeneration ||
|
|
1835
|
+
version.documentId !== obligation.documentId ||
|
|
1836
|
+
version.fileId !== document.fileId ||
|
|
1837
|
+
document.accountId !== input.accountId ||
|
|
1838
|
+
document.workspaceId !== input.workspaceId ||
|
|
1839
|
+
document.sourceExternalId !== obligation.externalObjectId ||
|
|
1840
|
+
!document.knowledgeSourceIdentity ||
|
|
1841
|
+
obligation.sourceLifecycleGeneration !== input.sourceLifecycleGeneration ||
|
|
1842
|
+
obligation.objectLifecycleGeneration !== input.objectLifecycleGeneration ||
|
|
1843
|
+
obligation.objectVersionGeneration !== input.objectVersionGeneration
|
|
1844
|
+
) {
|
|
1845
|
+
throw new Error("Google Drive ACL evidence authority changed");
|
|
1846
|
+
}
|
|
1847
|
+
if (input.eligibility === "eligible" && obligation.status !== "indexed") {
|
|
1848
|
+
throw new Error("Google Drive ACL eligibility requires a completed index obligation");
|
|
1849
|
+
}
|
|
1850
|
+
|
|
1851
|
+
const evidenceValues = {
|
|
1852
|
+
accountId: input.accountId,
|
|
1853
|
+
workspaceId: input.workspaceId,
|
|
1854
|
+
sourceId: obligation.sourceId,
|
|
1855
|
+
knowledgeSourceObjectId: obligation.knowledgeSourceObjectId,
|
|
1856
|
+
knowledgeDocumentVersionId: obligation.knowledgeDocumentVersionId,
|
|
1857
|
+
documentId: obligation.documentId,
|
|
1858
|
+
fileId: document.fileId,
|
|
1859
|
+
indexObligationId: obligation.id,
|
|
1860
|
+
connectionId: input.connectionId,
|
|
1861
|
+
connectionVersion: input.connectionVersion,
|
|
1862
|
+
sourcePrincipalHash,
|
|
1863
|
+
sourceSyncGeneration: input.sourceSyncGeneration,
|
|
1864
|
+
sourceConfigGeneration: input.sourceConfigGeneration,
|
|
1865
|
+
sourceLifecycleGeneration: input.sourceLifecycleGeneration,
|
|
1866
|
+
objectLifecycleGeneration: input.objectLifecycleGeneration,
|
|
1867
|
+
objectVersionGeneration: input.objectVersionGeneration,
|
|
1868
|
+
providerRevision: input.providerRevision,
|
|
1869
|
+
driveId: cleanBoundedString(input.driveId, 1024),
|
|
1870
|
+
aclRevision: exactBoundedString(input.aclRevision, 1024, "Google Drive ACL revision"),
|
|
1871
|
+
aclHash,
|
|
1872
|
+
eligibility: input.eligibility,
|
|
1873
|
+
observedAt,
|
|
1874
|
+
expiresAt,
|
|
1875
|
+
citationLocator: input.citationLocator,
|
|
1876
|
+
operationId: exactBoundedString(input.operationId, 256, "Google Drive ACL operation id"),
|
|
1877
|
+
inputHash,
|
|
1878
|
+
};
|
|
1879
|
+
const [inserted] = await tx
|
|
1880
|
+
.insert(schema.googleDriveObjectAclEvidence)
|
|
1881
|
+
.values(evidenceValues)
|
|
1882
|
+
.onConflictDoNothing({
|
|
1883
|
+
target: [
|
|
1884
|
+
schema.googleDriveObjectAclEvidence.indexObligationId,
|
|
1885
|
+
schema.googleDriveObjectAclEvidence.operationId,
|
|
1886
|
+
],
|
|
1887
|
+
})
|
|
1888
|
+
.returning();
|
|
1889
|
+
const [evidence] = inserted
|
|
1890
|
+
? [inserted]
|
|
1891
|
+
: await tx
|
|
1892
|
+
.select()
|
|
1893
|
+
.from(schema.googleDriveObjectAclEvidence)
|
|
1894
|
+
.where(
|
|
1895
|
+
and(
|
|
1896
|
+
eq(schema.googleDriveObjectAclEvidence.indexObligationId, obligation.id),
|
|
1897
|
+
eq(schema.googleDriveObjectAclEvidence.operationId, evidenceValues.operationId),
|
|
1898
|
+
),
|
|
1899
|
+
)
|
|
1900
|
+
.limit(1);
|
|
1901
|
+
if (!evidence || evidence.inputHash !== inputHash) {
|
|
1902
|
+
throw new Error("Google Drive ACL evidence operation conflicted");
|
|
1903
|
+
}
|
|
1904
|
+
if (inserted && principals.length > 0) {
|
|
1905
|
+
await tx.insert(schema.googleDriveObjectAclPrincipals).values(
|
|
1906
|
+
principals.map((principal, ordinal) => ({
|
|
1907
|
+
evidenceId: evidence.id,
|
|
1908
|
+
ordinal,
|
|
1909
|
+
accountId: input.accountId,
|
|
1910
|
+
workspaceId: input.workspaceId,
|
|
1911
|
+
...principal,
|
|
1912
|
+
})),
|
|
1913
|
+
);
|
|
1914
|
+
}
|
|
1915
|
+
const now = new Date();
|
|
1916
|
+
await tx
|
|
1917
|
+
.update(schema.knowledgeSourceSyncIndexObligations)
|
|
1918
|
+
.set({
|
|
1919
|
+
googleDriveAclEvidenceId: evidence.id,
|
|
1920
|
+
aclEligibility: input.eligibility,
|
|
1921
|
+
updatedAt: now,
|
|
1922
|
+
})
|
|
1923
|
+
.where(eq(schema.knowledgeSourceSyncIndexObligations.id, obligation.id));
|
|
1924
|
+
await tx
|
|
1925
|
+
.update(schema.knowledgeSourceSyncItemOutcomes)
|
|
1926
|
+
.set({
|
|
1927
|
+
aclEligibility: input.eligibility,
|
|
1928
|
+
aclEvidence: {
|
|
1929
|
+
version: 1,
|
|
1930
|
+
provider: "google_drive",
|
|
1931
|
+
evidenceId: evidence.id,
|
|
1932
|
+
aclRevision: evidence.aclRevision,
|
|
1933
|
+
observedAt: evidence.observedAt.toISOString(),
|
|
1934
|
+
expiresAt: evidence.expiresAt.toISOString(),
|
|
1935
|
+
},
|
|
1936
|
+
})
|
|
1937
|
+
.where(eq(schema.knowledgeSourceSyncItemOutcomes.indexObligationId, obligation.id));
|
|
1938
|
+
const [updatedDocument] = await tx
|
|
1939
|
+
.update(schema.documents)
|
|
1940
|
+
.set({
|
|
1941
|
+
agentAccess: input.eligibility === "eligible",
|
|
1942
|
+
...(input.eligibility === "denied" ? { chunkCount: 0 } : {}),
|
|
1943
|
+
updatedAt: now,
|
|
1944
|
+
})
|
|
1945
|
+
.where(eq(schema.documents.id, obligation.documentId))
|
|
1946
|
+
.returning({ id: schema.documents.id });
|
|
1947
|
+
if (!updatedDocument) {
|
|
1948
|
+
throw new Error("Google Drive ACL evidence document authority changed");
|
|
1949
|
+
}
|
|
1950
|
+
if (input.eligibility === "denied") {
|
|
1951
|
+
await tx
|
|
1952
|
+
.delete(schema.documentChunks)
|
|
1953
|
+
.where(eq(schema.documentChunks.documentId, obligation.documentId));
|
|
1954
|
+
}
|
|
1955
|
+
return { evidenceId: evidence.id, aclHash };
|
|
1956
|
+
}),
|
|
1957
|
+
);
|
|
1958
|
+
}
|
|
1959
|
+
|
|
1960
|
+
/**
|
|
1961
|
+
* Clear the current Drive authorization pointer before provider I/O without
|
|
1962
|
+
* destroying an otherwise valid index. All read surfaces immediately deny
|
|
1963
|
+
* because agent_access is false and the current evidence pointer is null. A
|
|
1964
|
+
* successful refresh can restore access without re-embedding; a final denied
|
|
1965
|
+
* observation uses recordGoogleDriveObjectAclEvidence and deletes chunks.
|
|
1966
|
+
*/
|
|
1967
|
+
export async function beginGoogleDriveObjectAclRefresh(
|
|
1968
|
+
db: Database,
|
|
1969
|
+
input: {
|
|
1970
|
+
accountId: string;
|
|
1971
|
+
workspaceId: string;
|
|
1972
|
+
obligationId: string;
|
|
1973
|
+
sourceSyncGeneration: number;
|
|
1974
|
+
sourceConfigGeneration: number;
|
|
1975
|
+
sourceLifecycleGeneration: number;
|
|
1976
|
+
objectLifecycleGeneration: number;
|
|
1977
|
+
objectVersionGeneration: number;
|
|
1978
|
+
},
|
|
1979
|
+
): Promise<void> {
|
|
1980
|
+
await withRlsContext(
|
|
1981
|
+
db,
|
|
1982
|
+
{ accountId: input.accountId, workspaceId: input.workspaceId },
|
|
1983
|
+
async (scopedDb) =>
|
|
1984
|
+
await scopedDb.transaction(async (tx) => {
|
|
1985
|
+
const [observed] = await tx
|
|
1986
|
+
.select()
|
|
1987
|
+
.from(schema.knowledgeSourceSyncIndexObligations)
|
|
1988
|
+
.where(eq(schema.knowledgeSourceSyncIndexObligations.id, input.obligationId))
|
|
1989
|
+
.limit(1);
|
|
1990
|
+
if (!observed) throw new Error("Google Drive ACL refresh obligation was not found");
|
|
1991
|
+
await setSubjectRlsContext(tx, observed.initiatingSubjectId);
|
|
1992
|
+
const [state] = await tx
|
|
1993
|
+
.select()
|
|
1994
|
+
.from(schema.knowledgeSourceSyncStates)
|
|
1995
|
+
.where(eq(schema.knowledgeSourceSyncStates.sourceId, observed.sourceId))
|
|
1996
|
+
.for("update")
|
|
1997
|
+
.limit(1);
|
|
1998
|
+
if (!state) throw new Error("Google Drive sync authorization is no longer active");
|
|
1999
|
+
await tx.execute(sql`
|
|
2000
|
+
SELECT knowledge_source_sync_lock_authority(
|
|
2001
|
+
${input.accountId}::uuid,
|
|
2002
|
+
${observed.sourceId}::uuid,
|
|
2003
|
+
${observed.knowledgeSourceObjectId}::uuid
|
|
2004
|
+
)
|
|
2005
|
+
`);
|
|
2006
|
+
const [[source], [object], [version], [obligation]] = await Promise.all([
|
|
2007
|
+
tx
|
|
2008
|
+
.select()
|
|
2009
|
+
.from(schema.knowledgeSources)
|
|
2010
|
+
.where(eq(schema.knowledgeSources.id, observed.sourceId))
|
|
2011
|
+
.limit(1),
|
|
2012
|
+
tx
|
|
2013
|
+
.select()
|
|
2014
|
+
.from(schema.knowledgeSourceObjects)
|
|
2015
|
+
.where(eq(schema.knowledgeSourceObjects.id, observed.knowledgeSourceObjectId))
|
|
2016
|
+
.limit(1),
|
|
2017
|
+
tx
|
|
2018
|
+
.select()
|
|
2019
|
+
.from(schema.knowledgeDocumentVersions)
|
|
2020
|
+
.where(eq(schema.knowledgeDocumentVersions.id, observed.knowledgeDocumentVersionId))
|
|
2021
|
+
.limit(1),
|
|
2022
|
+
tx
|
|
2023
|
+
.select()
|
|
2024
|
+
.from(schema.knowledgeSourceSyncIndexObligations)
|
|
2025
|
+
.where(eq(schema.knowledgeSourceSyncIndexObligations.id, input.obligationId))
|
|
2026
|
+
.for("update")
|
|
2027
|
+
.limit(1),
|
|
2028
|
+
]);
|
|
2029
|
+
const [document] = obligation
|
|
2030
|
+
? await tx
|
|
2031
|
+
.select()
|
|
2032
|
+
.from(schema.documents)
|
|
2033
|
+
.where(eq(schema.documents.id, obligation.documentId))
|
|
2034
|
+
.for("update")
|
|
2035
|
+
.limit(1)
|
|
2036
|
+
: [];
|
|
2037
|
+
if (
|
|
2038
|
+
!source ||
|
|
2039
|
+
!object ||
|
|
2040
|
+
!version ||
|
|
2041
|
+
!obligation ||
|
|
2042
|
+
!document ||
|
|
2043
|
+
state.reconnectRequired ||
|
|
2044
|
+
state.sourceSyncGeneration < input.sourceSyncGeneration ||
|
|
2045
|
+
state.sourceConfigGeneration !== input.sourceConfigGeneration ||
|
|
2046
|
+
state.sourceLifecycleGeneration !== input.sourceLifecycleGeneration ||
|
|
2047
|
+
source.lifecycleState !== "active" ||
|
|
2048
|
+
source.syncGeneration < obligation.sourceSyncGeneration ||
|
|
2049
|
+
source.lifecycleGeneration !== input.sourceLifecycleGeneration ||
|
|
2050
|
+
source.currentAclGeneration !== version.aclGeneration ||
|
|
2051
|
+
object.lifecycleState !== "active" ||
|
|
2052
|
+
object.lifecycleGeneration !== input.objectLifecycleGeneration ||
|
|
2053
|
+
object.versionGeneration !== input.objectVersionGeneration ||
|
|
2054
|
+
object.currentVersionId !== version.id ||
|
|
2055
|
+
version.documentId !== document.id ||
|
|
2056
|
+
version.fileId !== document.fileId ||
|
|
2057
|
+
obligation.sourceSyncGeneration !== input.sourceSyncGeneration ||
|
|
2058
|
+
obligation.sourceConfigGeneration !== input.sourceConfigGeneration ||
|
|
2059
|
+
obligation.sourceLifecycleGeneration !== input.sourceLifecycleGeneration ||
|
|
2060
|
+
obligation.objectLifecycleGeneration !== input.objectLifecycleGeneration ||
|
|
2061
|
+
obligation.objectVersionGeneration !== input.objectVersionGeneration ||
|
|
2062
|
+
obligation.status !== "indexed"
|
|
2063
|
+
) {
|
|
2064
|
+
throw new Error("Google Drive ACL refresh authority changed");
|
|
2065
|
+
}
|
|
2066
|
+
const now = new Date();
|
|
2067
|
+
await tx
|
|
2068
|
+
.update(schema.knowledgeSourceSyncIndexObligations)
|
|
2069
|
+
.set({
|
|
2070
|
+
googleDriveAclEvidenceId: null,
|
|
2071
|
+
aclEligibility: "denied",
|
|
2072
|
+
updatedAt: now,
|
|
2073
|
+
})
|
|
2074
|
+
.where(eq(schema.knowledgeSourceSyncIndexObligations.id, obligation.id));
|
|
2075
|
+
await tx
|
|
2076
|
+
.update(schema.knowledgeSourceSyncItemOutcomes)
|
|
2077
|
+
.set({
|
|
2078
|
+
aclEligibility: "denied",
|
|
2079
|
+
aclEvidence: { version: 1, provider: "google_drive", state: "refresh_pending" },
|
|
2080
|
+
})
|
|
2081
|
+
.where(eq(schema.knowledgeSourceSyncItemOutcomes.indexObligationId, obligation.id));
|
|
2082
|
+
await tx
|
|
2083
|
+
.update(schema.documents)
|
|
2084
|
+
.set({ agentAccess: false, updatedAt: now })
|
|
2085
|
+
.where(eq(schema.documents.id, obligation.documentId));
|
|
2086
|
+
}),
|
|
2087
|
+
);
|
|
2088
|
+
}
|
|
2089
|
+
|
|
2090
|
+
function normalizeGoogleDriveAclPrincipal(principal: GoogleDriveObjectAclPrincipal) {
|
|
2091
|
+
const permissionId = cleanBoundedString(principal.permissionId, 1024);
|
|
2092
|
+
const emailAddress = cleanBoundedString(principal.emailAddress, 320)?.toLowerCase() ?? null;
|
|
2093
|
+
const domain = cleanBoundedString(principal.domain, 320)?.toLowerCase() ?? null;
|
|
2094
|
+
if (principal.type === "user" || principal.type === "group") {
|
|
2095
|
+
if (!permissionId && !emailAddress) {
|
|
2096
|
+
throw new Error(`Google Drive ${principal.type} principal is missing identity`);
|
|
2097
|
+
}
|
|
2098
|
+
} else if (principal.type === "domain") {
|
|
2099
|
+
if (!domain) throw new Error("Google Drive domain principal is missing its domain");
|
|
2100
|
+
} else if (principal.type !== "anyone") {
|
|
2101
|
+
throw new Error("Google Drive ACL principal type is invalid");
|
|
2102
|
+
}
|
|
2103
|
+
const expirationTime = principal.expirationTime
|
|
2104
|
+
? exactDate(principal.expirationTime, "Google Drive ACL principal expiration")
|
|
2105
|
+
: null;
|
|
2106
|
+
return {
|
|
2107
|
+
principalType: principal.type,
|
|
2108
|
+
permissionIdHash: permissionId ? principalHash("permission", permissionId) : null,
|
|
2109
|
+
emailHash: emailAddress ? principalHash("email", emailAddress) : null,
|
|
2110
|
+
domainHash: domain ? principalHash("domain", domain) : null,
|
|
2111
|
+
role: principal.role,
|
|
2112
|
+
inherited: principal.inherited === true,
|
|
2113
|
+
allowFileDiscovery:
|
|
2114
|
+
principal.allowFileDiscovery === undefined ? null : principal.allowFileDiscovery,
|
|
2115
|
+
expirationTime,
|
|
2116
|
+
};
|
|
2117
|
+
}
|
|
2118
|
+
|
|
2119
|
+
function principalHash(kind: "permission" | "email" | "domain", value: string): string {
|
|
2120
|
+
return sha256Hex(`${kind}:${value.trim().toLowerCase()}`);
|
|
2121
|
+
}
|
|
2122
|
+
|
|
2123
|
+
function sha256Hex(value: string): string {
|
|
2124
|
+
return createHash("sha256").update(value).digest("hex");
|
|
2125
|
+
}
|
|
2126
|
+
|
|
2127
|
+
function exactDate(value: string, label: string): Date {
|
|
2128
|
+
const parsed = new Date(value);
|
|
2129
|
+
if (!value || !Number.isFinite(parsed.getTime()) || parsed.toISOString() !== value) {
|
|
2130
|
+
throw new Error(`${label} must be an exact UTC timestamp`);
|
|
2131
|
+
}
|
|
2132
|
+
return parsed;
|
|
2133
|
+
}
|
|
2134
|
+
|
|
2135
|
+
function exactBoundedString(value: unknown, max: number, label: string): string {
|
|
2136
|
+
const normalized = cleanBoundedString(value, max);
|
|
2137
|
+
if (!normalized || normalized !== value) throw new Error(`${label} is invalid`);
|
|
2138
|
+
return normalized;
|
|
2139
|
+
}
|
|
2140
|
+
|
|
2141
|
+
function cleanBoundedString(value: unknown, max: number): string | null {
|
|
2142
|
+
if (typeof value !== "string") return null;
|
|
2143
|
+
const normalized = value.trim();
|
|
2144
|
+
return normalized && normalized.length <= max ? normalized : null;
|
|
2145
|
+
}
|
|
2146
|
+
|
|
1617
2147
|
/** Bounded retention hook. Callers choose policy and cadence; pending index or
|
|
1618
2148
|
* ACL obligations are never deleted by age alone. */
|
|
1619
2149
|
export async function pruneKnowledgeSourceSyncEvidence(
|
package/src/provision-roles.ts
CHANGED
|
@@ -757,6 +757,42 @@ BEGIN
|
|
|
757
757
|
${literal(role)}
|
|
758
758
|
);
|
|
759
759
|
END IF;
|
|
760
|
+
-- Migration 0243 adds the exact Google Drive object authorization and
|
|
761
|
+
-- safe-citation projections. Re-converge both capabilities for the same
|
|
762
|
+
-- supported migrate-then-provision order without granting direct mutation
|
|
763
|
+
-- of their append-only ACL evidence tables.
|
|
764
|
+
IF to_regprocedure(
|
|
765
|
+
format(
|
|
766
|
+
'%I.google_drive_file_authorized(uuid,uuid,text,uuid)',
|
|
767
|
+
${literal(schema)}
|
|
768
|
+
)
|
|
769
|
+
) IS NOT NULL THEN
|
|
770
|
+
EXECUTE format(
|
|
771
|
+
'REVOKE ALL ON FUNCTION %I.google_drive_file_authorized(uuid, uuid, text, uuid) FROM PUBLIC',
|
|
772
|
+
${literal(schema)}
|
|
773
|
+
);
|
|
774
|
+
EXECUTE format(
|
|
775
|
+
'GRANT EXECUTE ON FUNCTION %I.google_drive_file_authorized(uuid, uuid, text, uuid) TO %I',
|
|
776
|
+
${literal(schema)},
|
|
777
|
+
${literal(role)}
|
|
778
|
+
);
|
|
779
|
+
END IF;
|
|
780
|
+
IF to_regprocedure(
|
|
781
|
+
format(
|
|
782
|
+
'%I.google_drive_document_citation(uuid,uuid,text,uuid,uuid)',
|
|
783
|
+
${literal(schema)}
|
|
784
|
+
)
|
|
785
|
+
) IS NOT NULL THEN
|
|
786
|
+
EXECUTE format(
|
|
787
|
+
'REVOKE ALL ON FUNCTION %I.google_drive_document_citation(uuid, uuid, text, uuid, uuid) FROM PUBLIC',
|
|
788
|
+
${literal(schema)}
|
|
789
|
+
);
|
|
790
|
+
EXECUTE format(
|
|
791
|
+
'GRANT EXECUTE ON FUNCTION %I.google_drive_document_citation(uuid, uuid, text, uuid, uuid) TO %I',
|
|
792
|
+
${literal(schema)},
|
|
793
|
+
${literal(role)}
|
|
794
|
+
);
|
|
795
|
+
END IF;
|
|
760
796
|
IF to_regprocedure(
|
|
761
797
|
format(
|
|
762
798
|
'%I.ensure_managed_human_personal_workspace(uuid,text,uuid)',
|
package/src/runtime-posture.ts
CHANGED
|
@@ -44,6 +44,22 @@ const DEDICATED_ARTIFACT_CAPABILITY_ROUTINES = new Set<string>([
|
|
|
44
44
|
|
|
45
45
|
const KNOWLEDGE_SOURCE_SYNC_LOCK_AUTHORITY_ROUTINE =
|
|
46
46
|
"knowledge_source_sync_lock_authority(uuid, uuid, uuid)";
|
|
47
|
+
const GOOGLE_DRIVE_FILE_AUTHORIZATION_ROUTINE =
|
|
48
|
+
"google_drive_file_authorized(uuid, uuid, text, uuid)";
|
|
49
|
+
const GOOGLE_DRIVE_DOCUMENT_CITATION_ROUTINE =
|
|
50
|
+
"google_drive_document_citation(uuid, uuid, text, uuid, uuid)";
|
|
51
|
+
const GOOGLE_DRIVE_AUTHORITY_TABLES = [
|
|
52
|
+
"connections",
|
|
53
|
+
"files",
|
|
54
|
+
"google_drive_object_acl_evidence",
|
|
55
|
+
"google_drive_object_acl_principals",
|
|
56
|
+
"knowledge_document_versions",
|
|
57
|
+
"knowledge_providers",
|
|
58
|
+
"knowledge_source_objects",
|
|
59
|
+
"knowledge_source_sync_index_obligations",
|
|
60
|
+
"knowledge_source_sync_states",
|
|
61
|
+
"knowledge_sources",
|
|
62
|
+
] as const;
|
|
47
63
|
const KNOWLEDGE_SOURCE_SYNC_LOCK_AUTHORITY_TABLES = [
|
|
48
64
|
"knowledge_sources",
|
|
49
65
|
"knowledge_source_objects",
|
|
@@ -110,6 +126,8 @@ export const RUNTIME_TARGET_SCHEMA_CAPABILITY_ROUTINES = [
|
|
|
110
126
|
XAI_AUTHORITY_LIVE_ROUTINE,
|
|
111
127
|
XAI_CREATE_CREDENTIAL_ROUTINE,
|
|
112
128
|
XAI_DISCONNECT_CREDENTIAL_ROUTINE,
|
|
129
|
+
GOOGLE_DRIVE_DOCUMENT_CITATION_ROUTINE,
|
|
130
|
+
GOOGLE_DRIVE_FILE_AUTHORIZATION_ROUTINE,
|
|
113
131
|
KNOWLEDGE_SOURCE_SYNC_LOCK_AUTHORITY_ROUTINE,
|
|
114
132
|
MANAGED_HUMAN_PERSONAL_WORKSPACE_ROUTINE,
|
|
115
133
|
...CANONICAL_HUMAN_IDENTITY_ROUTINES,
|
|
@@ -214,6 +232,8 @@ export const FORCE_RLS_TABLES = [
|
|
|
214
232
|
"generated_video_artifacts",
|
|
215
233
|
"github_installation_repositories",
|
|
216
234
|
"github_installations",
|
|
235
|
+
"google_drive_object_acl_evidence",
|
|
236
|
+
"google_drive_object_acl_principals",
|
|
217
237
|
"host_export_config",
|
|
218
238
|
"host_export_consumers",
|
|
219
239
|
"host_export_cursor_state",
|
|
@@ -579,6 +599,8 @@ export const RUNTIME_READ_INSERT_TABLES = [
|
|
|
579
599
|
"editable_artifact_transactions",
|
|
580
600
|
"editable_artifact_undo_claims",
|
|
581
601
|
"editable_artifact_versions",
|
|
602
|
+
"google_drive_object_acl_evidence",
|
|
603
|
+
"google_drive_object_acl_principals",
|
|
582
604
|
"knowledge_change_proposals",
|
|
583
605
|
"knowledge_claim_evidence",
|
|
584
606
|
"knowledge_claim_relations",
|
|
@@ -1260,7 +1282,33 @@ export function evaluateRuntimeDatabasePosture(
|
|
|
1260
1282
|
} else if (!routine.securityDefiner) {
|
|
1261
1283
|
violations.push(`target-schema runtime capability ${routine.name} is not SECURITY DEFINER`);
|
|
1262
1284
|
}
|
|
1263
|
-
if (
|
|
1285
|
+
if (
|
|
1286
|
+
routine.name === GOOGLE_DRIVE_FILE_AUTHORIZATION_ROUTINE ||
|
|
1287
|
+
routine.name === GOOGLE_DRIVE_DOCUMENT_CITATION_ROUTINE
|
|
1288
|
+
) {
|
|
1289
|
+
const missingAuthorityTables = GOOGLE_DRIVE_AUTHORITY_TABLES.filter(
|
|
1290
|
+
(tableName) => !tableByName.has(tableName),
|
|
1291
|
+
);
|
|
1292
|
+
if (missingAuthorityTables.length > 0) {
|
|
1293
|
+
violations.push(
|
|
1294
|
+
`target-schema runtime capability ${routine.name} authority tables are missing: ${missingAuthorityTables.join(", ")}`,
|
|
1295
|
+
);
|
|
1296
|
+
} else {
|
|
1297
|
+
const authorityTables = GOOGLE_DRIVE_AUTHORITY_TABLES.map(
|
|
1298
|
+
(tableName) => tableByName.get(tableName)!,
|
|
1299
|
+
);
|
|
1300
|
+
const authorityOwners = new Set(authorityTables.map((table) => table.owner));
|
|
1301
|
+
if (authorityOwners.size !== 1) {
|
|
1302
|
+
violations.push(
|
|
1303
|
+
`target-schema runtime capability ${routine.name} authority table owners do not match: ${authorityTables.map((table) => `${table.name}=${table.owner}`).join(", ")}`,
|
|
1304
|
+
);
|
|
1305
|
+
} else if (routine.owner !== authorityTables[0]!.owner) {
|
|
1306
|
+
violations.push(
|
|
1307
|
+
`target-schema runtime capability ${routine.name} owner ${routine.owner} does not match authority table owner ${authorityTables[0]!.owner}`,
|
|
1308
|
+
);
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
} else if (routine.name === KNOWLEDGE_SOURCE_SYNC_LOCK_AUTHORITY_ROUTINE) {
|
|
1264
1312
|
const missingAuthorityTables = KNOWLEDGE_SOURCE_SYNC_LOCK_AUTHORITY_TABLES.filter(
|
|
1265
1313
|
(tableName) => !tableByName.has(tableName),
|
|
1266
1314
|
);
|