Skip to content

Commit 88601af

Browse files
committed
Generate database tokens for existing projects
1 parent 021c7f3 commit 88601af

7 files changed

Lines changed: 118 additions & 10 deletions

File tree

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { authenticateDefaultUserInOpenOpsTables } from '@openops/common';
2+
import { encryptUtils } from '@openops/server-shared';
3+
import { MigrationInterface, QueryRunner } from 'typeorm';
4+
import { openopsTables } from '../../openops-tables';
5+
6+
export class AddTablesDatabaseTokenToProject1763394159990
7+
implements MigrationInterface
8+
{
9+
name = 'AddTablesDatabaseTokenToProject1763394159990';
10+
11+
public async up(queryRunner: QueryRunner): Promise<void> {
12+
await queryRunner.query(`
13+
ALTER TABLE "project"
14+
DROP COLUMN IF EXISTS "tablesToken";
15+
`);
16+
17+
await queryRunner.query(`
18+
ALTER TABLE "project"
19+
ADD COLUMN IF NOT EXISTS "tablesDatabaseToken" jsonb
20+
`);
21+
22+
await createTokensForExistingProjects(queryRunner);
23+
24+
await queryRunner.query(`
25+
ALTER TABLE "project"
26+
ALTER COLUMN "tablesDatabaseToken" SET NOT NULL
27+
`);
28+
}
29+
30+
public async down(_: QueryRunner): Promise<void> {
31+
throw new Error('Rollback not implemented');
32+
}
33+
}
34+
35+
async function createTokensForExistingProjects(
36+
queryRunner: QueryRunner,
37+
): Promise<void> {
38+
const projects = await queryRunner.query(
39+
'SELECT "id", "tablesWorkspaceId" FROM "project"',
40+
);
41+
42+
if (projects.length === 0) {
43+
return;
44+
}
45+
46+
const { token } = await authenticateDefaultUserInOpenOpsTables();
47+
for (const record of projects) {
48+
const newToken = await openopsTables.createDatabaseToken(
49+
record.tablesWorkspaceId,
50+
token,
51+
);
52+
const encryptTablesToken = encryptUtils.encryptString(newToken.key);
53+
54+
await queryRunner.query(
55+
`UPDATE "project" SET "tablesDatabaseToken" = $1 WHERE "id" = $2`,
56+
[encryptTablesToken, record.id],
57+
);
58+
}
59+
}

packages/server/api/src/app/database/postgres-connection.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ import { AddContentTypeToFolder1757331587268 } from './migrations/1757331587268-
3737
import { MigrateAiConfigToAppConnection1759242268873 } from './migrations/1759242268873-MigrateAiConfigToAppConnection';
3838
import { AddTestRunActionLimitsToFlowVersion1760429290001 } from './migrations/1760429290001-AddTestRunActionLimitsToFlowVersion';
3939
import { MoveTablesWorkspaceIdFromOrganizationToProject1760500000000 } from './migrations/1760500000000-MoveTablesWorkspaceIdFromOrganizationToProject';
40-
import { AddTablesTokenToProject1763131154284 } from './migrations/1763131154284-AddTablesTokenToProject';
40+
import { AddTablesDatabaseTokenToProject1763394159990 } from './migrations/1763394159990-AddTablesTokenToProject';
4141

4242
const getSslConfig = (): boolean | TlsOptions => {
4343
const useSsl = system.get(AppSystemProp.POSTGRES_USE_SSL);
@@ -83,7 +83,7 @@ const getMigrations = (): (new () => MigrationInterface)[] => {
8383
MigrateAiConfigToAppConnection1759242268873,
8484
AddTestRunActionLimitsToFlowVersion1760429290001,
8585
MoveTablesWorkspaceIdFromOrganizationToProject1760500000000,
86-
AddTablesTokenToProject1763131154284,
86+
AddTablesDatabaseTokenToProject1763394159990,
8787
];
8888
};
8989

packages/server/api/src/app/database/seeds/seed-admin.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,12 @@ export const upsertAdminUser = async (): Promise<void> => {
2020

2121
const user = await ensureUserExists(email, password);
2222

23-
const { workspaceId, databaseId } =
23+
const { workspaceId, databaseId, databaseToken } =
2424
await ensureOpenOpsTablesWorkspaceAndDatabaseExist();
2525

2626
await ensureOrganizationExists(user);
2727

28-
await ensureProjectExists(user, databaseId, workspaceId);
28+
await ensureProjectExists(user, databaseId, workspaceId, databaseToken);
2929
}
3030
};
3131

@@ -77,15 +77,16 @@ async function ensureUserExists(
7777
}
7878

7979
async function ensureOpenOpsTablesWorkspaceAndDatabaseExist(): Promise<{
80+
databaseToken: string;
8081
workspaceId: number;
8182
databaseId: number;
8283
}> {
8384
const { token } = await authenticateDefaultUserInOpenOpsTables();
8485

85-
const { workspaceId, databaseId } =
86+
const { workspaceId, databaseId, databaseToken } =
8687
await openopsTables.createDefaultWorkspaceAndDatabase(token);
8788

88-
if (!workspaceId || !databaseId) {
89+
if (!workspaceId || !databaseId || !databaseToken) {
8990
throw new Error('Failed to create OpenOps Tables workspace or database');
9091
}
9192

@@ -94,7 +95,7 @@ async function ensureOpenOpsTablesWorkspaceAndDatabaseExist(): Promise<{
9495
databaseId,
9596
});
9697

97-
return { workspaceId, databaseId };
98+
return { workspaceId, databaseId, databaseToken };
9899
}
99100

100101
async function ensureOrganizationExists(user: User): Promise<void> {
@@ -124,6 +125,7 @@ async function ensureProjectExists(
124125
user: User,
125126
databaseId: number,
126127
workspaceId: number,
128+
databaseToken: string,
127129
): Promise<void> {
128130
const project = await projectService.getOneForUser(user);
129131
if (project) {
@@ -148,6 +150,7 @@ async function ensureProjectExists(
148150
organizationId: user.organizationId!,
149151
tablesDatabaseId: databaseId,
150152
tablesWorkspaceId: workspaceId,
153+
tablesDatabaseToken: databaseToken,
151154
});
152155
}
153156

packages/server/api/src/app/openops-tables/default-workspace-database.ts

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,15 @@ export const OPENOPS_DEFAULT_WORKSPACE_NAME = 'OpenOps Workspace';
55

66
export async function createDefaultWorkspaceAndDatabase(
77
token: string,
8-
): Promise<{ workspaceId: number; databaseId: number }> {
8+
): Promise<{ workspaceId: number; databaseId: number; databaseToken: string }> {
99
const workspaceId = await getWorkspaceId(token);
1010

1111
const databaseId = await getDatabaseId(workspaceId, token);
1212

13+
const databaseToken = await getDatabaseToken(workspaceId, token);
14+
1315
return {
16+
databaseToken,
1417
workspaceId,
1518
databaseId,
1619
};
@@ -63,3 +66,31 @@ async function getDatabaseId(
6366

6467
return databaseId;
6568
}
69+
70+
async function getDatabaseToken(
71+
workspaceId: number,
72+
token: string,
73+
): Promise<string> {
74+
const databaseTokens = await openopsTables.listDatabaseTokens(
75+
workspaceId,
76+
token,
77+
);
78+
79+
let tablesToken = '';
80+
if (databaseTokens.length > 1) {
81+
throw new Error(
82+
'The user has multiple databases created in OpenOps Tables.',
83+
);
84+
} else if (databaseTokens.length === 1) {
85+
tablesToken = databaseTokens[0].key;
86+
} else {
87+
const newToken = await openopsTables.createDatabaseToken(
88+
workspaceId,
89+
token,
90+
);
91+
92+
tablesToken = newToken.key;
93+
}
94+
95+
return tablesToken;
96+
}

packages/server/api/src/app/project/project-entity.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
import { EntitySchema } from 'typeorm';
1010
import {
1111
BaseColumnSchemaPart,
12+
JSONB_COLUMN_TYPE,
1213
OpenOpsIdSchema,
1314
TIMESTAMP_COLUMN_TYPE,
1415
} from '../database/database-common';
@@ -46,6 +47,10 @@ export const ProjectEntity = new EntitySchema<ProjectSchema>({
4647
type: Number,
4748
nullable: false,
4849
},
50+
tablesDatabaseToken: {
51+
type: JSONB_COLUMN_TYPE,
52+
nullable: false,
53+
},
4954
},
5055
indices: [
5156
{

packages/server/api/src/app/project/project-service.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1-
import { rejectedPromiseHandler } from '@openops/server-shared';
1+
import { encryptUtils, rejectedPromiseHandler } from '@openops/server-shared';
22
import {
33
ApplicationError,
44
assertNotNullOrUndefined,
55
ErrorCode,
66
isNil,
77
openOpsId,
88
OpenOpsId,
9-
OrganizationRole,
109
Project,
1110
ProjectId,
1211
spreadIfDefined,
@@ -22,11 +21,17 @@ export const projectRepo = repoFactory(ProjectEntity);
2221

2322
export const projectService = {
2423
async create(params: CreateParams): Promise<Project> {
24+
const encryptTablesToken = encryptUtils.encryptString(
25+
params.tablesDatabaseToken,
26+
);
2527
const newProject: NewProject = {
2628
id: openOpsId(),
2729
...params,
30+
tablesDatabaseToken: encryptTablesToken,
2831
};
32+
2933
const savedProject = await projectRepo().save(newProject);
34+
3035
rejectedPromiseHandler(projectHooks.getHooks().postCreate(savedProject));
3136
return savedProject;
3237
},
@@ -159,6 +164,7 @@ type CreateParams = {
159164
externalId?: string;
160165
tablesDatabaseId: number;
161166
tablesWorkspaceId: number;
167+
tablesDatabaseToken: string;
162168
};
163169

164170
type AddProjectToOrganizationParams = {

packages/shared/src/lib/project/project.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ export const Project = Type.Object({
3434
organizationId: OpenOpsId,
3535
tablesDatabaseId: Type.Integer(),
3636
tablesWorkspaceId: Type.Integer(),
37+
tablesDatabaseToken: Type.Object({
38+
iv: Type.String(),
39+
data: Type.String(),
40+
}),
3741
});
3842

3943
export type Project = Static<typeof Project>;

0 commit comments

Comments
 (0)