mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-14 19:44:52 +00:00
feat(server): completed user multi role with temporary access
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
import { TableName, TProjectUserMembershipRolesInsert } from "../schemas";
|
||||
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
const doesTableExist = await knex.schema.hasTable(TableName.ProjectUserMembershipRole);
|
||||
if (!doesTableExist) {
|
||||
await knex.schema.createTable(TableName.ProjectUserMembershipRole, (t) => {
|
||||
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
||||
t.string("role").notNullable();
|
||||
t.uuid("projectMembershipId").notNullable();
|
||||
t.foreign("projectMembershipId").references("id").inTable(TableName.ProjectMembership).onDelete("CASCADE");
|
||||
// until role is changed/removed the role should not deleted
|
||||
t.uuid("customRoleId");
|
||||
t.foreign("customRoleId").references("id").inTable(TableName.ProjectRoles);
|
||||
t.boolean("isTemporary").notNullable().defaultTo(false);
|
||||
t.string("temporaryMode");
|
||||
t.string("temporaryRange"); // could be cron or relative time like 1H or 1minute etc
|
||||
t.datetime("temporaryAccessStartTime");
|
||||
t.datetime("temporaryAccessEndTime");
|
||||
t.timestamps(true, true, true);
|
||||
});
|
||||
|
||||
await createOnUpdateTrigger(knex, TableName.ProjectUserMembershipRole);
|
||||
|
||||
const projectMembershipStream = knex.select("*").from(TableName.ProjectMembership).stream();
|
||||
const chunkSize = 1000;
|
||||
let rows: TProjectUserMembershipRolesInsert[] = [];
|
||||
for await (const row of projectMembershipStream) {
|
||||
// disabling eslint just this part because the latest ts type doesn't have these values after migration as they are removed
|
||||
/* eslint-disable */
|
||||
// @ts-ignore - created at is inserted from old data
|
||||
rows = rows.concat({
|
||||
// @ts-ignore - missing in ts type post migration
|
||||
role: row.role,
|
||||
// @ts-ignore - missing in ts type post migration
|
||||
customRoleId: row.roleId,
|
||||
projectMembershipId: row.id,
|
||||
createdAt: row.createdAt,
|
||||
updatedAt: row.updatedAt
|
||||
});
|
||||
/* eslint-disable */
|
||||
if (rows.length >= chunkSize) {
|
||||
await knex(TableName.ProjectUserMembershipRole).insert(rows);
|
||||
rows.splice(0, rows.length);
|
||||
}
|
||||
}
|
||||
await knex(TableName.ProjectUserMembershipRole).insert(rows);
|
||||
await knex.schema.alterTable(TableName.ProjectMembership, (t) => {
|
||||
t.dropColumn("roleId");
|
||||
t.dropColumn("role");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
const projectUserMembershipRoleStream = knex.select("*").from(TableName.ProjectUserMembershipRole).stream();
|
||||
await knex.schema.alterTable(TableName.ProjectMembership, (t) => {
|
||||
t.string("role");
|
||||
t.uuid("roleId");
|
||||
t.foreign("roleId").references("id").inTable(TableName.ProjectRoles);
|
||||
});
|
||||
for await (const row of projectUserMembershipRoleStream) {
|
||||
await knex(TableName.ProjectMembership).where({ id: row.projectMembershipId }).update({
|
||||
// @ts-ignore - since the latest one doesn't have roleId anymore there will be type error here
|
||||
roleId: row.customRoleId,
|
||||
role: row.role
|
||||
});
|
||||
}
|
||||
await knex.schema.alterTable(TableName.ProjectMembership, (t) => {
|
||||
t.string("role").notNullable().alter({ alterNullable: true });
|
||||
});
|
||||
|
||||
await knex.schema.dropTableIfExists(TableName.ProjectUserMembershipRole);
|
||||
await dropOnUpdateTrigger(knex, TableName.ProjectUserMembershipRole);
|
||||
}
|
||||
@@ -25,6 +25,7 @@ export * from "./project-environments";
|
||||
export * from "./project-keys";
|
||||
export * from "./project-memberships";
|
||||
export * from "./project-roles";
|
||||
export * from "./project-user-membership-roles";
|
||||
export * from "./projects";
|
||||
export * from "./saml-configs";
|
||||
export * from "./scim-tokens";
|
||||
|
||||
@@ -20,6 +20,7 @@ export enum TableName {
|
||||
Environment = "project_environments",
|
||||
ProjectMembership = "project_memberships",
|
||||
ProjectRoles = "project_roles",
|
||||
ProjectUserMembershipRole = "project_user_membership_roles",
|
||||
ProjectKeys = "project_keys",
|
||||
Secret = "secrets",
|
||||
SecretBlindIndex = "secret_blind_indexes",
|
||||
|
||||
@@ -9,12 +9,10 @@ import { TImmutableDBKeys } from "./models";
|
||||
|
||||
export const ProjectMembershipsSchema = z.object({
|
||||
id: z.string().uuid(),
|
||||
role: z.string(),
|
||||
createdAt: z.date(),
|
||||
updatedAt: z.date(),
|
||||
userId: z.string().uuid(),
|
||||
projectId: z.string(),
|
||||
roleId: z.string().uuid().nullable().optional()
|
||||
projectId: z.string()
|
||||
});
|
||||
|
||||
export type TProjectMemberships = z.infer<typeof ProjectMembershipsSchema>;
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
// Code generated by automation script, DO NOT EDIT.
|
||||
// Automated by pulling database and generating zod schema
|
||||
// To update. Just run npm run generate:schema
|
||||
// Written by akhilmhdh.
|
||||
|
||||
import { z } from "zod";
|
||||
|
||||
import { TImmutableDBKeys } from "./models";
|
||||
|
||||
export const ProjectUserMembershipRolesSchema = z.object({
|
||||
id: z.string().uuid(),
|
||||
role: z.string(),
|
||||
projectMembershipId: z.string().uuid(),
|
||||
customRoleId: z.string().uuid().nullable().optional(),
|
||||
isTemporary: z.boolean().default(false),
|
||||
temporaryMode: z.string().nullable().optional(),
|
||||
temporaryRange: z.string().nullable().optional(),
|
||||
temporaryAccessStartTime: z.date().nullable().optional(),
|
||||
temporaryAccessEndTime: z.date().nullable().optional(),
|
||||
createdAt: z.date(),
|
||||
updatedAt: z.date()
|
||||
});
|
||||
|
||||
export type TProjectUserMembershipRoles = z.infer<typeof ProjectUserMembershipRolesSchema>;
|
||||
export type TProjectUserMembershipRolesInsert = Omit<
|
||||
z.input<typeof ProjectUserMembershipRolesSchema>,
|
||||
TImmutableDBKeys
|
||||
>;
|
||||
export type TProjectUserMembershipRolesUpdate = Partial<
|
||||
Omit<z.input<typeof ProjectUserMembershipRolesSchema>, TImmutableDBKeys>
|
||||
>;
|
||||
@@ -4,7 +4,7 @@ import { Knex } from "knex";
|
||||
|
||||
import { encryptSymmetric128BitHexKeyUTF8 } from "@app/lib/crypto";
|
||||
|
||||
import { OrgMembershipRole, SecretEncryptionAlgo, SecretKeyEncoding, TableName } from "../schemas";
|
||||
import { ProjectMembershipRole, SecretEncryptionAlgo, SecretKeyEncoding, TableName } from "../schemas";
|
||||
import { buildUserProjectKey, getUserPrivateKey, seedData1 } from "../seed-data";
|
||||
|
||||
export const DEFAULT_PROJECT_ENVS = [
|
||||
@@ -30,10 +30,15 @@ export async function seed(knex: Knex): Promise<void> {
|
||||
})
|
||||
.returning("*");
|
||||
|
||||
await knex(TableName.ProjectMembership).insert({
|
||||
projectId: project.id,
|
||||
role: OrgMembershipRole.Admin,
|
||||
userId: seedData1.id
|
||||
const projectMembership = await knex(TableName.ProjectMembership)
|
||||
.insert({
|
||||
projectId: project.id,
|
||||
userId: seedData1.id
|
||||
})
|
||||
.returning("*");
|
||||
await knex(TableName.ProjectUserMembershipRole).insert({
|
||||
role: ProjectMembershipRole.Admin,
|
||||
projectMembershipId: projectMembership[0].id
|
||||
});
|
||||
|
||||
const user = await knex(TableName.UserEncryptionKey).where({ userId: seedData1.id }).first();
|
||||
|
||||
Reference in New Issue
Block a user