@highstate/backend 0.9.16 → 0.9.19
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/chunk-5WVU2AK4.js +1535 -0
- package/dist/chunk-5WVU2AK4.js.map +1 -0
- package/dist/chunk-I7BWSAN6.js +49 -0
- package/dist/chunk-I7BWSAN6.js.map +1 -0
- package/dist/{chunk-RCB4AFGD.js → chunk-VB4YL327.js} +51 -71
- package/dist/chunk-VB4YL327.js.map +1 -0
- package/dist/database/local/prisma.config.js +26 -0
- package/dist/database/local/prisma.config.js.map +1 -0
- package/dist/highstate.manifest.json +5 -4
- package/dist/index.js +7676 -6634
- package/dist/index.js.map +1 -1
- package/dist/library/package-resolution-worker.js +8 -6
- package/dist/library/package-resolution-worker.js.map +1 -1
- package/dist/library/worker/main.js +63 -58
- package/dist/library/worker/main.js.map +1 -1
- package/dist/shared/index.js +3 -216
- package/dist/shared/index.js.map +1 -1
- package/package.json +23 -11
- package/prisma/backend/_schema/layout.prisma +7 -0
- package/prisma/backend/_schema/library.prisma +17 -0
- package/prisma/backend/_schema/project.prisma +101 -0
- package/prisma/backend/_schema/pulumi.prisma +17 -0
- package/prisma/backend/postgresql/main.prisma +17 -0
- package/prisma/backend/sqlite/main.prisma +17 -0
- package/prisma/backend/sqlite/migrations/20250817070609_initiial/migration.sql +34 -0
- package/prisma/backend/sqlite/migrations/20250817104948_add_fields/migration.sql +59 -0
- package/prisma/backend/sqlite/migrations/20250818082732_add_models/migration.sql +41 -0
- package/prisma/backend/sqlite/migrations/20250818083106_a/migration.sql +19 -0
- package/prisma/backend/sqlite/migrations/20250818101945_hi/migration.sql +1 -0
- package/prisma/backend/sqlite/migrations/20250819082315_a/migration.sql +5 -0
- package/prisma/backend/sqlite/migrations/migration_lock.toml +3 -0
- package/prisma/project/api-key.prisma +27 -0
- package/prisma/project/artifact.prisma +52 -0
- package/prisma/project/custom-status.prisma +46 -0
- package/prisma/project/evaluation.prisma +35 -0
- package/prisma/project/instance.prisma +160 -0
- package/prisma/project/layout.prisma +23 -0
- package/prisma/project/lock.prisma +18 -0
- package/prisma/project/main.prisma +17 -0
- package/prisma/project/migrations/20250816081310_initial/migration.sql +300 -0
- package/prisma/project/migrations/20250816082523_test/migration.sql +72 -0
- package/prisma/project/migrations/20250818065643_update/migration.sql +42 -0
- package/prisma/project/migrations/20250818070758_a/migration.sql +8 -0
- package/prisma/project/migrations/20250818070913_a/migration.sql +8 -0
- package/prisma/project/migrations/20250818082720_add_motels/migration.sql +11 -0
- package/prisma/project/migrations/20250818112523_hello/migration.sql +35 -0
- package/prisma/project/migrations/20250819082305_a/migration.sql +14 -0
- package/prisma/project/migrations/20250819165004_add_missing_fields/migration.sql +216 -0
- package/prisma/project/migrations/20250819171309_a/migration.sql +22 -0
- package/prisma/project/migrations/20250820113949_a/migration.sql +66 -0
- package/prisma/project/migrations/20250820144256_b/migration.sql +31 -0
- package/prisma/project/migrations/20250820145547_a/migration.sql +24 -0
- package/prisma/project/migrations/20250820182517_b/migration.sql +2 -0
- package/prisma/project/migrations/20250821172324_a/migration.sql +2 -0
- package/prisma/project/migrations/20250822081339_a/migration.sql +219 -0
- package/prisma/project/migrations/20250822083742_b/migration.sql +1 -0
- package/prisma/project/migrations/20250822105134_boom/migration.sql +1 -0
- package/prisma/project/migrations/20250822141028_b/migration.sql +1 -0
- package/prisma/project/migrations/20250822142342_b/migration.sql +16 -0
- package/prisma/project/migrations/20250824072720_a/migration.sql +1 -0
- package/prisma/project/migrations/20250824093656_b/migration.sql +21 -0
- package/prisma/project/migrations/20250825082518_a/migration.sql +1 -0
- package/prisma/project/migrations/20250825085343_b/migration.sql +1 -0
- package/prisma/project/migrations/20250825091312_a/migration.sql +1 -0
- package/prisma/project/migrations/20250903095431_hi/migration.sql +44 -0
- package/prisma/project/migrations/20250903174255_a/migration.sql +24 -0
- package/prisma/project/migrations/20250908095205_hi/migration.sql +18 -0
- package/prisma/project/migrations/20250909155857_hi/migration.sql +15 -0
- package/prisma/project/migrations/migration_lock.toml +3 -0
- package/prisma/project/model.prisma +37 -0
- package/prisma/project/operation.prisma +148 -0
- package/prisma/project/page.prisma +41 -0
- package/prisma/project/secret.prisma +42 -0
- package/prisma/project/service-account.prisma +36 -0
- package/prisma/project/terminal.prisma +90 -0
- package/prisma/project/trigger.prisma +31 -0
- package/prisma/project/unlock-method.prisma +32 -0
- package/prisma/project/worker.prisma +138 -0
- package/src/artifact/abstractions.ts +13 -13
- package/src/artifact/encryption.ts +31 -15
- package/src/artifact/factory.ts +7 -10
- package/src/artifact/local.ts +33 -50
- package/src/business/api-key.ts +24 -36
- package/src/business/artifact.test.ts +978 -0
- package/src/business/artifact.ts +136 -215
- package/src/business/evaluation.ts +328 -0
- package/src/business/index.ts +5 -1
- package/src/business/instance-lock.test.ts +1060 -0
- package/src/business/instance-lock.ts +387 -77
- package/src/business/instance-state.test.ts +735 -0
- package/src/business/instance-state.ts +604 -217
- package/src/business/operation.test.ts +439 -0
- package/src/business/operation.ts +174 -208
- package/src/business/project-model.ts +258 -0
- package/src/business/project-unlock.ts +172 -112
- package/src/business/project.ts +407 -0
- package/src/business/secret.test.ts +513 -0
- package/src/business/secret.ts +194 -131
- package/src/business/settings.test.ts +695 -0
- package/src/business/settings.ts +855 -0
- package/src/business/terminal-session.ts +90 -0
- package/src/business/unit-extra.test.ts +539 -0
- package/src/business/unit-extra.ts +160 -0
- package/src/business/worker.test.ts +391 -0
- package/src/business/worker.ts +250 -114
- package/src/common/codebase.ts +65 -0
- package/src/common/index.ts +3 -2
- package/src/common/logger.ts +5 -0
- package/src/common/utils.ts +4 -3
- package/src/config.ts +15 -12
- package/src/database/_generated/backend/postgresql/client.ts +72 -0
- package/src/database/_generated/backend/postgresql/commonInputTypes.ts +350 -0
- package/src/database/_generated/backend/postgresql/enums.ts +13 -0
- package/src/database/_generated/backend/postgresql/internal/class.ts +320 -0
- package/src/database/_generated/backend/postgresql/internal/prismaNamespace.ts +1238 -0
- package/src/database/_generated/backend/postgresql/models/Library.ts +1263 -0
- package/src/database/_generated/backend/postgresql/models/Project.ts +2175 -0
- package/src/database/_generated/backend/postgresql/models/ProjectModelStorage.ts +1263 -0
- package/src/database/_generated/backend/postgresql/models/ProjectSpace.ts +1602 -0
- package/src/database/_generated/backend/postgresql/models/PulumiBackend.ts +1263 -0
- package/src/database/_generated/backend/postgresql/models/UserWorkspaseLayout.ts +1065 -0
- package/src/database/_generated/backend/postgresql/models.ts +16 -0
- package/src/database/_generated/backend/postgresql/pjtg.ts +182 -0
- package/src/database/_generated/backend/sqlite/client.ts +72 -0
- package/src/database/_generated/backend/sqlite/commonInputTypes.ts +331 -0
- package/src/database/_generated/backend/sqlite/enums.ts +13 -0
- package/src/database/_generated/backend/sqlite/internal/class.ts +318 -0
- package/src/database/_generated/backend/sqlite/internal/prismaNamespace.ts +1207 -0
- package/src/database/_generated/backend/sqlite/models/Library.ts +1261 -0
- package/src/database/_generated/backend/sqlite/models/Project.ts +2169 -0
- package/src/database/_generated/backend/sqlite/models/ProjectModelStorage.ts +1261 -0
- package/src/database/_generated/backend/sqlite/models/ProjectSpace.ts +1599 -0
- package/src/database/_generated/backend/sqlite/models/PulumiBackend.ts +1261 -0
- package/src/database/_generated/backend/sqlite/models/UserWorkspaseLayout.ts +1063 -0
- package/src/database/_generated/backend/sqlite/models.ts +16 -0
- package/src/database/_generated/backend/sqlite/pjtg.ts +182 -0
- package/src/database/_generated/project/client.ts +204 -0
- package/src/database/_generated/project/commonInputTypes.ts +827 -0
- package/src/database/_generated/project/enums.ts +104 -0
- package/src/database/_generated/project/internal/class.ts +479 -0
- package/src/database/_generated/project/internal/prismaNamespace.ts +2974 -0
- package/src/database/_generated/project/models/ApiKey.ts +1506 -0
- package/src/database/_generated/project/models/Artifact.ts +2051 -0
- package/src/database/_generated/project/models/HubModel.ts +1125 -0
- package/src/database/_generated/project/models/InstanceCustomStatus.ts +1713 -0
- package/src/database/_generated/project/models/InstanceEvaluationState.ts +1312 -0
- package/src/database/_generated/project/models/InstanceLock.ts +1268 -0
- package/src/database/_generated/project/models/InstanceModel.ts +1125 -0
- package/src/database/_generated/project/models/InstanceOperationState.ts +1707 -0
- package/src/database/_generated/project/models/InstanceState.ts +4613 -0
- package/src/database/_generated/project/models/Operation.ts +1647 -0
- package/src/database/_generated/project/models/OperationLog.ts +1455 -0
- package/src/database/_generated/project/models/Page.ts +1838 -0
- package/src/database/_generated/project/models/Secret.ts +1692 -0
- package/src/database/_generated/project/models/ServiceAccount.ts +2165 -0
- package/src/database/_generated/project/models/Terminal.ts +2038 -0
- package/src/database/_generated/project/models/TerminalSession.ts +1454 -0
- package/src/database/_generated/project/models/TerminalSessionLog.ts +1280 -0
- package/src/database/_generated/project/models/Trigger.ts +1430 -0
- package/src/database/_generated/project/models/UnlockMethod.ts +1220 -0
- package/src/database/_generated/project/models/UserCompositeViewport.ts +1280 -0
- package/src/database/_generated/project/models/UserProjectViewport.ts +1059 -0
- package/src/database/_generated/project/models/Worker.ts +1459 -0
- package/src/database/_generated/project/models/WorkerUnitRegistration.ts +1524 -0
- package/src/database/_generated/project/models/WorkerVersion.ts +1974 -0
- package/src/database/_generated/project/models/WorkerVersionLog.ts +1318 -0
- package/src/database/_generated/project/models.ts +35 -0
- package/src/database/_generated/project/pjtg.ts +182 -0
- package/src/database/abstractions.ts +19 -0
- package/src/database/factory.ts +37 -0
- package/src/database/index.ts +6 -0
- package/src/database/local/backend.ts +134 -0
- package/src/database/local/index.ts +3 -0
- package/src/database/local/meta.ts +46 -0
- package/src/database/local/prisma.config.ts +25 -0
- package/src/database/local/project.ts +39 -0
- package/src/database/manager.ts +181 -0
- package/src/database/migrate.ts +35 -0
- package/src/database/prisma.ts +56 -0
- package/src/database/well-known.ts +38 -0
- package/src/index.ts +4 -4
- package/src/library/abstractions.ts +21 -14
- package/src/library/factory.ts +1 -1
- package/src/library/local.ts +86 -38
- package/src/library/package-resolution-worker.ts +1 -1
- package/src/library/worker/evaluator.ts +61 -48
- package/src/library/worker/loader.lite.ts +14 -1
- package/src/library/worker/main.ts +9 -16
- package/src/library/worker/protocol.ts +0 -12
- package/src/lock/manager.ts +12 -7
- package/src/orchestrator/manager.ts +198 -131
- package/src/orchestrator/operation-context.ts +357 -0
- package/src/orchestrator/operation-plan.destroy.test.md +357 -0
- package/src/orchestrator/operation-plan.destroy.test.ts +775 -0
- package/src/orchestrator/operation-plan.fixtures.ts +213 -0
- package/src/orchestrator/operation-plan.md +198 -0
- package/src/orchestrator/operation-plan.refresh.test.md +199 -0
- package/src/orchestrator/operation-plan.refresh.test.ts +367 -0
- package/src/orchestrator/operation-plan.ts +709 -0
- package/src/orchestrator/operation-plan.update.test.md +485 -0
- package/src/orchestrator/operation-plan.update.test.ts +1066 -0
- package/src/orchestrator/operation-workset.ts +235 -583
- package/src/orchestrator/operation.ts +446 -904
- package/src/orchestrator/plan-test-builder.ts +267 -0
- package/src/project-model/abstractions.ts +118 -0
- package/src/project-model/backends/codebase.ts +365 -0
- package/src/project-model/backends/database.ts +440 -0
- package/src/project-model/errors.ts +81 -0
- package/src/project-model/factory.ts +24 -0
- package/src/project-model/index.ts +4 -0
- package/src/project-model/utils.test.ts +544 -0
- package/src/project-model/utils.ts +242 -0
- package/src/pubsub/abstractions.ts +10 -1
- package/src/pubsub/factory.ts +4 -4
- package/src/pubsub/index.ts +1 -0
- package/src/pubsub/manager.ts +49 -25
- package/src/pubsub/memory.ts +31 -0
- package/src/runner/abstractions.ts +38 -26
- package/src/runner/artifact-env.ts +17 -6
- package/src/runner/factory.ts +6 -6
- package/src/runner/force-abort.ts +3 -6
- package/src/runner/local.ts +79 -72
- package/src/runner/pulumi.ts +26 -63
- package/src/services.ts +214 -103
- package/src/shared/models/backend/index.ts +3 -1
- package/src/shared/models/backend/library.ts +12 -4
- package/src/shared/models/backend/project.ts +43 -23
- package/src/shared/models/backend/pulumi.ts +14 -0
- package/src/shared/models/backend/unlock-method.ts +1 -1
- package/src/shared/models/backend/well-known.ts +58 -0
- package/src/shared/models/base.ts +40 -109
- package/src/shared/models/errors.ts +82 -1
- package/src/shared/models/index.ts +3 -2
- package/src/shared/models/prisma.ts +36 -0
- package/src/shared/models/project/api-key.ts +37 -56
- package/src/shared/models/project/artifact.ts +15 -105
- package/src/shared/models/project/custom-status.ts +12 -0
- package/src/shared/models/project/index.ts +9 -9
- package/src/shared/models/project/lock.ts +10 -78
- package/src/shared/models/project/model.ts +32 -0
- package/src/shared/models/project/operation.ts +222 -99
- package/src/shared/models/project/page.ts +37 -48
- package/src/shared/models/project/secret.ts +29 -103
- package/src/shared/models/project/service-account.ts +12 -17
- package/src/shared/models/project/state.ts +100 -390
- package/src/shared/models/project/terminal.ts +75 -89
- package/src/shared/models/project/trigger.ts +13 -49
- package/src/shared/models/project/unlock-method.ts +21 -20
- package/src/shared/models/project/worker.ts +89 -88
- package/src/shared/resolvers/graph-resolver.ts +62 -26
- package/src/shared/resolvers/index.ts +1 -1
- package/src/shared/resolvers/input-hash.ts +24 -14
- package/src/shared/resolvers/input.ts +48 -6
- package/src/shared/resolvers/registry.ts +5 -4
- package/src/shared/resolvers/state.ts +12 -1
- package/src/shared/resolvers/validation.ts +29 -9
- package/src/shared/utils/index.ts +1 -1
- package/src/shared/utils/promise-tracker.ts +30 -3
- package/src/terminal/abstractions.ts +1 -1
- package/src/terminal/docker.ts +3 -3
- package/src/terminal/manager.ts +102 -118
- package/src/test-utils/database.ts +119 -0
- package/src/test-utils/index.ts +2 -0
- package/src/test-utils/services.ts +134 -0
- package/src/unlock/abstractions.ts +31 -0
- package/src/unlock/index.ts +2 -0
- package/src/unlock/memory.ts +27 -0
- package/src/worker/abstractions.ts +7 -4
- package/src/worker/docker.ts +14 -19
- package/src/worker/manager.ts +376 -79
- package/dist/chunk-RCB4AFGD.js.map +0 -1
- package/dist/chunk-WHALQHEZ.js +0 -2017
- package/dist/chunk-WHALQHEZ.js.map +0 -1
- package/src/business/backend-unlock.ts +0 -10
- package/src/common/performance.ts +0 -44
- package/src/hotstate/abstractions.ts +0 -48
- package/src/hotstate/factory.ts +0 -17
- package/src/hotstate/index.ts +0 -3
- package/src/hotstate/manager.ts +0 -192
- package/src/hotstate/memory.ts +0 -100
- package/src/hotstate/validation.ts +0 -101
- package/src/project/abstractions.ts +0 -102
- package/src/project/factory.ts +0 -11
- package/src/project/index.ts +0 -3
- package/src/project/local.ts +0 -469
- package/src/project/manager.ts +0 -574
- package/src/pubsub/local.ts +0 -36
- package/src/pubsub/validation.ts +0 -33
- package/src/shared/models/project/component.ts +0 -45
- package/src/shared/models/project/instance.ts +0 -74
- package/src/state/abstractions.ts +0 -450
- package/src/state/encryption.ts +0 -59
- package/src/state/factory.ts +0 -20
- package/src/state/index.ts +0 -6
- package/src/state/local/backend.ts +0 -299
- package/src/state/local/collection.ts +0 -342
- package/src/state/local/index.ts +0 -2
- package/src/state/manager.ts +0 -819
- package/src/state/repository/index.ts +0 -2
- package/src/state/repository/repository.index.ts +0 -193
- package/src/state/repository/repository.ts +0 -458
- /package/src/{state → database/local}/keyring.ts +0 -0
|
@@ -1,37 +1,43 @@
|
|
|
1
|
-
import type { StateBatch, StateManager } from "../state"
|
|
2
1
|
import type { Logger } from "pino"
|
|
3
|
-
import type {
|
|
2
|
+
import type { DatabaseManager } from "../database"
|
|
4
3
|
import type { PubSubManager } from "../pubsub"
|
|
4
|
+
import type { ProjectUnlockBackend } from "../unlock"
|
|
5
5
|
import { randomBytes } from "node:crypto"
|
|
6
6
|
import { armor, Decrypter, Encrypter } from "age-encryption"
|
|
7
|
-
import {
|
|
7
|
+
import { z } from "zod"
|
|
8
|
+
import { createProjectLogger } from "../common"
|
|
9
|
+
import {
|
|
10
|
+
CannotDeleteLastUnlockMethodError,
|
|
11
|
+
ProjectNotFoundError,
|
|
12
|
+
type ProjectUnlockState,
|
|
13
|
+
type ProjectUnlockSuite,
|
|
14
|
+
type UnlockMethodInput,
|
|
15
|
+
} from "../shared"
|
|
8
16
|
|
|
9
17
|
type UnlockTask = {
|
|
10
18
|
name: string
|
|
11
19
|
handler: (projectId: string) => Promise<void> | void
|
|
12
20
|
}
|
|
13
21
|
|
|
22
|
+
export const projectUnlockServiceConfig = z.object({
|
|
23
|
+
HIGHSTATE_ENCRYPTION_ENABLED: z.stringbool().default(true),
|
|
24
|
+
HIGHSTATE_DEV_AUTO_UNLOCK_PROJECT_IDS: z
|
|
25
|
+
.string()
|
|
26
|
+
.transform(val => val?.split(",") ?? [])
|
|
27
|
+
.default([]),
|
|
28
|
+
})
|
|
29
|
+
|
|
14
30
|
export class ProjectUnlockService {
|
|
15
31
|
constructor(
|
|
16
|
-
private readonly
|
|
17
|
-
private readonly hotStateManager: HotStateManager,
|
|
32
|
+
private readonly database: DatabaseManager,
|
|
18
33
|
private readonly pubsubManager: PubSubManager,
|
|
34
|
+
private readonly projectUnlockBackend: ProjectUnlockBackend,
|
|
35
|
+
private readonly config: z.infer<typeof projectUnlockServiceConfig>,
|
|
19
36
|
private readonly logger: Logger,
|
|
20
37
|
) {}
|
|
21
38
|
|
|
22
39
|
private readonly unlockTasks: UnlockTask[] = []
|
|
23
40
|
|
|
24
|
-
/**
|
|
25
|
-
* Checks if the project is unlocked.
|
|
26
|
-
* A project is considered locked if it has no master key set in memory.
|
|
27
|
-
*
|
|
28
|
-
* @param projectId The ID of the project to check.
|
|
29
|
-
* @returns True if the project is locked, false otherwise.
|
|
30
|
-
*/
|
|
31
|
-
async isProjectUnlocked(projectId: string): Promise<boolean> {
|
|
32
|
-
return await this.hotStateManager.exists(["project-master-key", projectId])
|
|
33
|
-
}
|
|
34
|
-
|
|
35
41
|
/**
|
|
36
42
|
* Gets the current unlock state of the project.
|
|
37
43
|
* If the project is unlocked, it returns an object with type "unlocked".
|
|
@@ -41,41 +47,59 @@ export class ProjectUnlockService {
|
|
|
41
47
|
* @returns The unlock state of the project.
|
|
42
48
|
*/
|
|
43
49
|
async getProjectUnlockState(projectId: string): Promise<ProjectUnlockState> {
|
|
44
|
-
const isUnlocked = await this.
|
|
50
|
+
const isUnlocked = await this.projectUnlockBackend.checkProjectUnlocked(projectId)
|
|
45
51
|
if (isUnlocked) {
|
|
46
52
|
return { type: "unlocked" }
|
|
47
53
|
}
|
|
48
54
|
|
|
49
|
-
const
|
|
55
|
+
const project = await this.database.backend.project.findUnique({
|
|
56
|
+
where: { id: projectId },
|
|
57
|
+
select: { unlockSuite: true },
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
if (!project) {
|
|
61
|
+
throw new ProjectNotFoundError(projectId)
|
|
62
|
+
}
|
|
63
|
+
|
|
50
64
|
return {
|
|
51
65
|
type: "locked",
|
|
52
|
-
unlockSuite,
|
|
66
|
+
unlockSuite: project.unlockSuite,
|
|
53
67
|
}
|
|
54
68
|
}
|
|
55
69
|
|
|
56
70
|
/**
|
|
57
|
-
*
|
|
71
|
+
* Sets up the project database by creating a master key and unlock suite with the provided unlock method.
|
|
72
|
+
*
|
|
73
|
+
* Creates databases, configures encryption and persists the unlock method inside the project database.
|
|
74
|
+
*
|
|
75
|
+
* Then returns the encrypted master key and the unlock suite for further persisting in the backend database.
|
|
58
76
|
*
|
|
59
77
|
* @param projectId The ID of the project to create the state for.
|
|
60
78
|
* @param unlockMethod The unlock method to use to encrypt the master key. Should be provided by the frontend.
|
|
61
79
|
*/
|
|
62
|
-
async
|
|
80
|
+
async setupProjectDatabase(
|
|
81
|
+
projectId: string,
|
|
82
|
+
unlockMethodInput: UnlockMethodInput,
|
|
83
|
+
): Promise<[encryptedMasterKey: string, unlockSuite: ProjectUnlockSuite]> {
|
|
84
|
+
// generate a new master key for the project
|
|
63
85
|
const masterKey = randomBytes(32)
|
|
64
|
-
await using batch = this.stateManager.batch()
|
|
65
86
|
|
|
66
|
-
//
|
|
67
|
-
await this.
|
|
87
|
+
// set the master key to setup the database encryption
|
|
88
|
+
await this.projectUnlockBackend.unlockProject(projectId, masterKey)
|
|
89
|
+
|
|
90
|
+
const encryptedMasterKey = await this.encryptProjectMasterKey(projectId, [unlockMethodInput])
|
|
68
91
|
|
|
69
|
-
|
|
70
|
-
await this.updateUnlockSuite(projectId, [unlockMethod], batch)
|
|
71
|
-
await this.updateEncryptedMasterKey(projectId, [unlockMethod], batch, masterKey)
|
|
92
|
+
const database = await this.database.setupDatabase(projectId)
|
|
72
93
|
|
|
73
|
-
// persist
|
|
74
|
-
await
|
|
75
|
-
.getUnlockMethodRepository(projectId)
|
|
76
|
-
.put(unlockMethod.id, unlockMethod, batch)
|
|
94
|
+
// persist unlock method (now we can do it since the database is set up and unlocked)
|
|
95
|
+
await database.unlockMethod.create({ data: unlockMethodInput })
|
|
77
96
|
|
|
78
|
-
|
|
97
|
+
const unlockSuite: ProjectUnlockSuite = {
|
|
98
|
+
encryptedIdentities: [unlockMethodInput.encryptedIdentity],
|
|
99
|
+
hasPasskey: unlockMethodInput.type === "passkey",
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return [encryptedMasterKey, unlockSuite]
|
|
79
103
|
}
|
|
80
104
|
|
|
81
105
|
/**
|
|
@@ -85,7 +109,7 @@ export class ProjectUnlockService {
|
|
|
85
109
|
* @param decryptedIdentity The decrypted identity to use for unlocking the project. Should be provided by the frontend.
|
|
86
110
|
*/
|
|
87
111
|
async unlockProject(projectId: string, decryptedIdentity: string): Promise<void> {
|
|
88
|
-
if (await this.
|
|
112
|
+
if (await this.projectUnlockBackend.checkProjectUnlocked(projectId)) {
|
|
89
113
|
this.logger.warn(
|
|
90
114
|
{ projectId },
|
|
91
115
|
`project "%s" is already unlocked, skipping unlock operation`,
|
|
@@ -95,12 +119,24 @@ export class ProjectUnlockService {
|
|
|
95
119
|
}
|
|
96
120
|
|
|
97
121
|
// load the encrypted master key for the project
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
|
|
122
|
+
const project = await this.database.backend.project.findUnique({
|
|
123
|
+
where: { id: projectId },
|
|
124
|
+
select: { encryptedMasterKey: true },
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
if (!project) {
|
|
128
|
+
throw new ProjectNotFoundError(projectId)
|
|
101
129
|
}
|
|
102
130
|
|
|
103
|
-
|
|
131
|
+
if (!this.config.HIGHSTATE_ENCRYPTION_ENABLED) {
|
|
132
|
+
// no cryptography, just unlock with an empty master key
|
|
133
|
+
await this.projectUnlockBackend.unlockProject(projectId, Buffer.alloc(0))
|
|
134
|
+
await this.pubsubManager.publish(["project-unlock-state", projectId], { type: "unlocked" })
|
|
135
|
+
await this.runUnlockTasks(projectId)
|
|
136
|
+
return
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const encryptedMasterKey = armor.decode(project.encryptedMasterKey)
|
|
104
140
|
|
|
105
141
|
const decrypter = new Decrypter()
|
|
106
142
|
decrypter.addIdentity(decryptedIdentity)
|
|
@@ -108,20 +144,8 @@ export class ProjectUnlockService {
|
|
|
108
144
|
// decrypt the master key using the provided identity
|
|
109
145
|
const masterKey = await decrypter.decrypt(encryptedMasterKey)
|
|
110
146
|
|
|
111
|
-
//
|
|
112
|
-
await this.
|
|
113
|
-
|
|
114
|
-
// load instance states to the hot state
|
|
115
|
-
// TODO: this should be done by something else, ideally lazy-loaded when needed
|
|
116
|
-
const instanceStates = await this.stateManager
|
|
117
|
-
.getInstanceStateRepository(projectId)
|
|
118
|
-
.getAllItems()
|
|
119
|
-
|
|
120
|
-
await this.hotStateManager.hmset(
|
|
121
|
-
["instance-states", projectId],
|
|
122
|
-
instanceStates.map(state => [state.id, state]),
|
|
123
|
-
)
|
|
124
|
-
|
|
147
|
+
// unlock the project in the backend
|
|
148
|
+
await this.projectUnlockBackend.unlockProject(projectId, Buffer.from(masterKey))
|
|
125
149
|
await this.pubsubManager.publish(["project-unlock-state", projectId], { type: "unlocked" })
|
|
126
150
|
|
|
127
151
|
// run unlock tasks
|
|
@@ -143,24 +167,37 @@ export class ProjectUnlockService {
|
|
|
143
167
|
* The project must be unlocked.
|
|
144
168
|
*
|
|
145
169
|
* @param projectId The ID of the project to add the unlock method to.
|
|
146
|
-
* @param
|
|
170
|
+
* @param inputUnlockMethod The unlock method to add. Should be provided by the frontend.
|
|
147
171
|
*/
|
|
148
|
-
async addProjectUnlockMethod(
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
await this.
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
172
|
+
async addProjectUnlockMethod(
|
|
173
|
+
projectId: string,
|
|
174
|
+
inputUnlockMethod: UnlockMethodInput,
|
|
175
|
+
): Promise<void> {
|
|
176
|
+
const database = await this.database.forProject(projectId)
|
|
177
|
+
|
|
178
|
+
await database.$transaction(async tx => {
|
|
179
|
+
// 1. fetch all unlock method recipients for the project
|
|
180
|
+
const unlockMethods = await tx.unlockMethod.findMany({
|
|
181
|
+
select: { type: true, recipient: true, encryptedIdentity: true },
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
const allUnlockMethods = [...unlockMethods, inputUnlockMethod]
|
|
185
|
+
|
|
186
|
+
// 2. encrypt the project data for all recipients + the new recipient
|
|
187
|
+
const encryptedMasterKey = await this.encryptProjectMasterKey(projectId, allUnlockMethods)
|
|
188
|
+
|
|
189
|
+
// 3. persist the new unlock method
|
|
190
|
+
await tx.unlockMethod.create({ data: inputUnlockMethod })
|
|
191
|
+
|
|
192
|
+
// 4. update the project with the new master key and unlock suite
|
|
193
|
+
await this.database.backend.project.update({
|
|
194
|
+
where: { id: projectId },
|
|
195
|
+
data: {
|
|
196
|
+
encryptedMasterKey,
|
|
197
|
+
unlockSuite: ProjectUnlockService.createUnlockSuite(allUnlockMethods),
|
|
198
|
+
},
|
|
199
|
+
})
|
|
200
|
+
})
|
|
164
201
|
}
|
|
165
202
|
|
|
166
203
|
/**
|
|
@@ -171,25 +208,34 @@ export class ProjectUnlockService {
|
|
|
171
208
|
* @param unlockMethodId The ID of the unlock method to remove.
|
|
172
209
|
*/
|
|
173
210
|
async removeProjectUnlockMethod(projectId: string, unlockMethodId: string): Promise<void> {
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
// get all existing unlock methods
|
|
177
|
-
let existingMethods = await this.stateManager.getUnlockMethodRepository(projectId).getAllItems()
|
|
178
|
-
|
|
179
|
-
// do not allow removing the last unlock method under any circumstances
|
|
180
|
-
if (existingMethods.length === 1) {
|
|
181
|
-
throw new Error("Rejected removing the last unlock method!")
|
|
182
|
-
}
|
|
211
|
+
const database = await this.database.forProject(projectId)
|
|
183
212
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
213
|
+
await database.$transaction(async tx => {
|
|
214
|
+
// 1. fetch all unlock methods except the one to remove
|
|
215
|
+
const unlockMethods = await tx.unlockMethod.findMany({
|
|
216
|
+
where: { id: { not: unlockMethodId } },
|
|
217
|
+
select: { type: true, recipient: true, encryptedIdentity: true },
|
|
218
|
+
})
|
|
187
219
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
220
|
+
if (unlockMethods.length === 0) {
|
|
221
|
+
throw new CannotDeleteLastUnlockMethodError(projectId)
|
|
222
|
+
}
|
|
191
223
|
|
|
192
|
-
|
|
224
|
+
// 2. encrypt the project data for remaining recipients
|
|
225
|
+
const encryptedMasterKey = await this.encryptProjectMasterKey(projectId, unlockMethods)
|
|
226
|
+
|
|
227
|
+
// 3. delete the unlock method
|
|
228
|
+
await tx.unlockMethod.delete({ where: { id: unlockMethodId } })
|
|
229
|
+
|
|
230
|
+
// 4. update the project with the new master key and unlock suite
|
|
231
|
+
await this.database.backend.project.update({
|
|
232
|
+
where: { id: projectId },
|
|
233
|
+
data: {
|
|
234
|
+
encryptedMasterKey,
|
|
235
|
+
unlockSuite: ProjectUnlockService.createUnlockSuite(unlockMethods),
|
|
236
|
+
},
|
|
237
|
+
})
|
|
238
|
+
})
|
|
193
239
|
}
|
|
194
240
|
|
|
195
241
|
/**
|
|
@@ -203,40 +249,54 @@ export class ProjectUnlockService {
|
|
|
203
249
|
this.unlockTasks.push({ name, handler })
|
|
204
250
|
}
|
|
205
251
|
|
|
206
|
-
private async
|
|
252
|
+
private async encryptProjectMasterKey(
|
|
207
253
|
projectId: string,
|
|
208
|
-
unlockMethods:
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
encryptedIdentities: unlockMethods.map(method => method.encryptedIdentity),
|
|
216
|
-
hasPasskey: unlockMethods.some(method => method.type === "passkey"),
|
|
217
|
-
},
|
|
218
|
-
batch,
|
|
219
|
-
)
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
private async updateEncryptedMasterKey(
|
|
223
|
-
projectId: string,
|
|
224
|
-
unlockMethods: UnlockMethod[],
|
|
225
|
-
batch: StateBatch,
|
|
226
|
-
masterKey?: Uint8Array,
|
|
227
|
-
): Promise<void> {
|
|
228
|
-
masterKey ??= await this.stateManager.getProjectMasterKey(projectId)
|
|
254
|
+
unlockMethods: { recipient: string }[],
|
|
255
|
+
): Promise<string> {
|
|
256
|
+
const masterKey = await this.database.getProjectMasterKey(projectId)
|
|
257
|
+
if (!masterKey) {
|
|
258
|
+
// окак
|
|
259
|
+
return "encryption disabled"
|
|
260
|
+
}
|
|
229
261
|
|
|
262
|
+
// encrypt the master key for all unlock methods
|
|
230
263
|
const encrypter = new Encrypter()
|
|
231
|
-
for (const
|
|
232
|
-
encrypter.addRecipient(
|
|
264
|
+
for (const unlockMethod of unlockMethods) {
|
|
265
|
+
encrypter.addRecipient(unlockMethod.recipient)
|
|
233
266
|
}
|
|
234
267
|
|
|
235
|
-
// encrypt the master key for all unlock methods
|
|
236
268
|
const encryptedMasterKey = await encrypter.encrypt(masterKey)
|
|
237
|
-
|
|
238
|
-
// set the encrypted master key in the backend
|
|
239
269
|
const armoredMasterKey = armor.encode(encryptedMasterKey)
|
|
240
|
-
|
|
270
|
+
|
|
271
|
+
return armoredMasterKey
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Auto-unlocks the projects for the development environment.
|
|
276
|
+
*/
|
|
277
|
+
async autoUnlockProjects(): Promise<void> {
|
|
278
|
+
// just mark the projects as unlocked with an empty master key and run unlock tasks
|
|
279
|
+
for (const projectId of this.config.HIGHSTATE_DEV_AUTO_UNLOCK_PROJECT_IDS) {
|
|
280
|
+
const logger = createProjectLogger(this.logger, projectId)
|
|
281
|
+
|
|
282
|
+
try {
|
|
283
|
+
logger.info("auto-unlocking project (dev mode)")
|
|
284
|
+
|
|
285
|
+
await this.projectUnlockBackend.unlockProject(projectId, Buffer.alloc(0))
|
|
286
|
+
await this.pubsubManager.publish(["project-unlock-state", projectId], { type: "unlocked" })
|
|
287
|
+
await this.runUnlockTasks(projectId)
|
|
288
|
+
} catch (error) {
|
|
289
|
+
logger.error({ error }, "failed to auto-unlock project")
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
private static createUnlockSuite(
|
|
295
|
+
unlockMethods: { type: string; encryptedIdentity: string }[],
|
|
296
|
+
): ProjectUnlockSuite {
|
|
297
|
+
return {
|
|
298
|
+
encryptedIdentities: unlockMethods.map(method => method.encryptedIdentity),
|
|
299
|
+
hasPasskey: unlockMethods.some(method => method.type === "passkey"),
|
|
300
|
+
}
|
|
241
301
|
}
|
|
242
302
|
}
|