Skip to content

Commit 443288e

Browse files
committed
Migration: add instance_admin permission record for default user (#9823)
1 parent e72c68e commit 443288e

File tree

3 files changed

+237
-1
lines changed

3 files changed

+237
-1
lines changed

airbyte-bootloader/src/test/java/io/airbyte/bootloader/BootloaderTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ class BootloaderTest {
9292

9393
// ⚠️ This line should change with every new migration to show that you meant to make a new
9494
// migration to the prod database
95-
private static final String CURRENT_CONFIGS_MIGRATION_VERSION = "0.50.33.004";
95+
private static final String CURRENT_CONFIGS_MIGRATION_VERSION = "0.50.33.005";
9696
private static final String CURRENT_JOBS_MIGRATION_VERSION = "0.50.4.001";
9797
private static final String CDK_VERSION = "1.2.3";
9898

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
* Copyright (c) 2023 Airbyte, Inc., all rights reserved.
3+
*/
4+
5+
package io.airbyte.db.instance.configs.migrations;
6+
7+
import static org.jooq.impl.DSL.select;
8+
9+
import io.airbyte.db.instance.configs.migrations.V0_50_16_001__UpdateEnumTypeAuthProviderAndPermissionType.PermissionType;
10+
import java.util.UUID;
11+
import org.flywaydb.core.api.migration.BaseJavaMigration;
12+
import org.flywaydb.core.api.migration.Context;
13+
import org.jooq.DSLContext;
14+
import org.jooq.Field;
15+
import org.jooq.impl.DSL;
16+
import org.jooq.impl.SQLDataType;
17+
import org.slf4j.Logger;
18+
import org.slf4j.LoggerFactory;
19+
20+
public class V0_50_33_005__CreateInstanceAdminPermissionForDefaultUser extends BaseJavaMigration {
21+
22+
private static final String PERMISSION_TABLE = "permission";
23+
private static final String USER_TABLE = "\"user\"";
24+
// The all-zero UUID is used to reliably identify the default user.
25+
private static final UUID DEFAULT_USER_ID = UUID.fromString("00000000-0000-0000-0000-000000000000");
26+
private static final Field<UUID> ID_COLUMN = DSL.field("id", SQLDataType.UUID);
27+
private static final Field<UUID> USER_ID_COLUMN = DSL.field("user_id", SQLDataType.UUID);
28+
private static final Field<PermissionType> PERMISSION_TYPE_COLUMN =
29+
DSL.field("permission_type", SQLDataType.VARCHAR.asEnumDataType(PermissionType.class));
30+
31+
private static final Logger LOGGER = LoggerFactory.getLogger(V0_50_33_005__CreateInstanceAdminPermissionForDefaultUser.class);
32+
33+
@Override
34+
public void migrate(final Context context) throws Exception {
35+
LOGGER.info("Running migration: {}", this.getClass().getSimpleName());
36+
37+
// Warning: please do not use any jOOQ generated code to write a migration.
38+
// As database schema changes, the generated jOOQ code can be deprecated. So
39+
// old migration may not compile if there is any generated code.
40+
final DSLContext ctx = DSL.using(context.getConnection());
41+
createInstanceAdminPermissionForDefaultUser(ctx);
42+
}
43+
44+
static void createInstanceAdminPermissionForDefaultUser(final DSLContext ctx) {
45+
// return early if the default user is not present in the database. This
46+
// shouldn't happen in practice, but if somebody manually removed the default
47+
// user prior to this migration, we want this to be a no-op.
48+
if (!ctx.fetchExists(select()
49+
.from(DSL.table(USER_TABLE))
50+
.where(ID_COLUMN.eq(DEFAULT_USER_ID)))) {
51+
LOGGER.warn("Default user does not exist. Skipping this migration.");
52+
return;
53+
}
54+
55+
// return early if the default user already has an instance_admin permission.
56+
// This shouldn't happen in practice, but if somebody manually inserted a
57+
// permission record prior to this migration, we want this to be a no-op.
58+
if (ctx.fetchExists(select()
59+
.from(DSL.table(PERMISSION_TABLE))
60+
.where(USER_ID_COLUMN.eq(DEFAULT_USER_ID))
61+
.and(PERMISSION_TYPE_COLUMN.eq(PermissionType.INSTANCE_ADMIN)))) {
62+
LOGGER.warn("Default user already has instance_admin permission. Skipping this migration.");
63+
return;
64+
}
65+
66+
LOGGER.info("Inserting instance_admin permission record for default user.");
67+
ctx.insertInto(DSL.table(PERMISSION_TABLE),
68+
ID_COLUMN,
69+
USER_ID_COLUMN,
70+
PERMISSION_TYPE_COLUMN)
71+
.values(UUID.randomUUID(), DEFAULT_USER_ID, PermissionType.INSTANCE_ADMIN)
72+
.execute();
73+
}
74+
75+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
/*
2+
* Copyright (c) 2023 Airbyte, Inc., all rights reserved.
3+
*/
4+
5+
package io.airbyte.db.instance.configs.migrations;
6+
7+
import static org.junit.jupiter.api.Assertions.assertEquals;
8+
import static org.junit.jupiter.api.Assertions.assertNotNull;
9+
import static org.junit.jupiter.api.Assertions.assertNull;
10+
11+
import io.airbyte.db.factory.FlywayFactory;
12+
import io.airbyte.db.instance.configs.AbstractConfigsDatabaseTest;
13+
import io.airbyte.db.instance.configs.ConfigsDatabaseMigrator;
14+
import io.airbyte.db.instance.configs.migrations.V0_50_16_001__UpdateEnumTypeAuthProviderAndPermissionType.PermissionType;
15+
import io.airbyte.db.instance.development.DevDatabaseMigrator;
16+
import java.util.UUID;
17+
import org.flywaydb.core.Flyway;
18+
import org.flywaydb.core.api.migration.BaseJavaMigration;
19+
import org.jooq.DSLContext;
20+
import org.jooq.Field;
21+
import org.jooq.impl.DSL;
22+
import org.jooq.impl.SQLDataType;
23+
import org.junit.jupiter.api.AfterEach;
24+
import org.junit.jupiter.api.BeforeEach;
25+
import org.junit.jupiter.api.Test;
26+
27+
public class V0_50_33_005__CreateInstanceAdminPermissionForDefaultUser_Test extends AbstractConfigsDatabaseTest {
28+
29+
private static final UUID DEFAULT_USER_ID = UUID.fromString("00000000-0000-0000-0000-000000000000");
30+
// The user table is quoted to avoid conflict with the reserved user keyword in Postgres.
31+
private static final String USER_TABLE = "\"user\"";
32+
private static final String PERMISSION_TABLE = "permission";
33+
private static final Field<UUID> ID_COLUMN = DSL.field("id", SQLDataType.UUID);
34+
private static final Field<UUID> USER_ID_COLUMN = DSL.field("user_id", SQLDataType.UUID);
35+
private static final Field<PermissionType> PERMISSION_TYPE_COLUMN =
36+
DSL.field("permission_type", SQLDataType.VARCHAR.asEnumDataType(PermissionType.class));
37+
38+
@BeforeEach
39+
void beforeEach() {
40+
final Flyway flyway =
41+
FlywayFactory.create(dataSource, "V0_50_33_005__CreateInstanceAdminPermissionForDefaultUser", ConfigsDatabaseMigrator.DB_IDENTIFIER,
42+
ConfigsDatabaseMigrator.MIGRATION_FILE_LOCATION);
43+
final ConfigsDatabaseMigrator configsDbMigrator = new ConfigsDatabaseMigrator(database, flyway);
44+
45+
final BaseJavaMigration previousMigration = new V0_50_33_004__AddSecretPersistenceTypeColumnAndAlterConstraint();
46+
final DevDatabaseMigrator devConfigsDbMigrator = new DevDatabaseMigrator(configsDbMigrator, previousMigration.getVersion());
47+
devConfigsDbMigrator.createBaseline();
48+
}
49+
50+
@AfterEach
51+
void afterEach() {
52+
// Making sure we reset between tests
53+
dslContext.dropSchemaIfExists("public").cascade().execute();
54+
dslContext.createSchema("public").execute();
55+
dslContext.setSchema("public").execute();
56+
}
57+
58+
@Test
59+
void testMigrationDefaultState() {
60+
final DSLContext ctx = getDslContext();
61+
62+
// a prior migration should have already inserted the default user
63+
var userRecord = ctx.selectFrom(DSL.table(USER_TABLE))
64+
.where(ID_COLUMN.eq(DEFAULT_USER_ID))
65+
.fetchOne();
66+
assertNotNull(userRecord);
67+
68+
var instanceAdminPermission = ctx.selectFrom(DSL.table(PERMISSION_TABLE))
69+
.where(USER_ID_COLUMN.eq(DEFAULT_USER_ID))
70+
.and(PERMISSION_TYPE_COLUMN.eq(PermissionType.INSTANCE_ADMIN))
71+
.fetchOne();
72+
73+
// instance_admin record should not yet exist for the default user
74+
assertNull(instanceAdminPermission);
75+
76+
// run the migration
77+
V0_50_33_005__CreateInstanceAdminPermissionForDefaultUser.createInstanceAdminPermissionForDefaultUser(ctx);
78+
79+
// verify that an instance_admin permission record was written to the database for the default user
80+
instanceAdminPermission = ctx.selectFrom(DSL.table(PERMISSION_TABLE))
81+
.where(USER_ID_COLUMN.eq(DEFAULT_USER_ID))
82+
.and(PERMISSION_TYPE_COLUMN.eq(PermissionType.INSTANCE_ADMIN))
83+
.fetchOne();
84+
85+
assertNotNull(instanceAdminPermission);
86+
}
87+
88+
@Test
89+
void testMigrationNoDefaultUser() {
90+
final DSLContext ctx = getDslContext();
91+
92+
// a prior migration should have already inserted the default user
93+
var userRecord = ctx.selectFrom(DSL.table(USER_TABLE))
94+
.where(ID_COLUMN.eq(DEFAULT_USER_ID))
95+
.fetchOne();
96+
assertNotNull(userRecord);
97+
98+
var instanceAdminPermission = ctx.selectFrom(DSL.table(PERMISSION_TABLE))
99+
.where(USER_ID_COLUMN.eq(DEFAULT_USER_ID))
100+
.and(PERMISSION_TYPE_COLUMN.eq(PermissionType.INSTANCE_ADMIN))
101+
.fetchOne();
102+
103+
// instance_admin record should not yet exist for the default user
104+
assertNull(instanceAdminPermission);
105+
106+
// remove the default user to simulate this non-conventional state
107+
ctx.deleteFrom(DSL.table(USER_TABLE))
108+
.where(ID_COLUMN.eq(DEFAULT_USER_ID))
109+
.execute();
110+
111+
// record the count of permission records at this time
112+
final var permissionCountBeforeMigration = ctx.fetchCount(DSL.table(PERMISSION_TABLE));
113+
114+
// run the migration
115+
V0_50_33_005__CreateInstanceAdminPermissionForDefaultUser.createInstanceAdminPermissionForDefaultUser(ctx);
116+
117+
// verify that the permission record count is unchanged because this should be a no-op.
118+
final var permissionCountAfterMigration = ctx.fetchCount(DSL.table(PERMISSION_TABLE));
119+
120+
assertEquals(permissionCountBeforeMigration, permissionCountAfterMigration);
121+
}
122+
123+
@Test
124+
void testMigrationAlreadyInstanceAdmin() {
125+
final DSLContext ctx = getDslContext();
126+
127+
// a prior migration should have already inserted the default user
128+
var userRecord = ctx.selectFrom(DSL.table(USER_TABLE))
129+
.where(ID_COLUMN.eq(DEFAULT_USER_ID))
130+
.fetchOne();
131+
assertNotNull(userRecord);
132+
133+
var instanceAdminPermission = ctx.selectFrom(DSL.table(PERMISSION_TABLE))
134+
.where(USER_ID_COLUMN.eq(DEFAULT_USER_ID))
135+
.and(PERMISSION_TYPE_COLUMN.eq(PermissionType.INSTANCE_ADMIN))
136+
.fetchOne();
137+
138+
// instance_admin record should not yet exist for the default user
139+
assertNull(instanceAdminPermission);
140+
141+
// manually insert an instance_admin permission record for the default user
142+
ctx.insertInto(DSL.table(PERMISSION_TABLE),
143+
ID_COLUMN,
144+
USER_ID_COLUMN,
145+
PERMISSION_TYPE_COLUMN)
146+
.values(UUID.randomUUID(), DEFAULT_USER_ID, PermissionType.INSTANCE_ADMIN)
147+
.execute();
148+
149+
// record the count of permission records at this time
150+
final var permissionCountBeforeMigration = ctx.fetchCount(DSL.table(PERMISSION_TABLE));
151+
152+
// run the migration
153+
V0_50_33_005__CreateInstanceAdminPermissionForDefaultUser.createInstanceAdminPermissionForDefaultUser(ctx);
154+
155+
// verify that the permission record count is unchanged because this should be a no-op.
156+
final var permissionCountAfterMigration = ctx.fetchCount(DSL.table(PERMISSION_TABLE));
157+
158+
assertEquals(permissionCountBeforeMigration, permissionCountAfterMigration);
159+
}
160+
161+
}

0 commit comments

Comments
 (0)