feat: added migration for new memberships

This commit is contained in:
=
2025-09-26 15:04:18 +05:30
parent f116ff0f42
commit 6dca5b3fa2
9 changed files with 338 additions and 0 deletions

View File

@@ -17,6 +17,9 @@ import {
TAccessApprovalRequestsReviewersInsert,
TAccessApprovalRequestsReviewersUpdate,
TAccessApprovalRequestsUpdate,
TAdditionalPrivileges,
TAdditionalPrivilegesInsert,
TAdditionalPrivilegesUpdate,
TApiKeys,
TApiKeysInsert,
TApiKeysUpdate,
@@ -227,6 +230,15 @@ import {
TLdapGroupMaps,
TLdapGroupMapsInsert,
TLdapGroupMapsUpdate,
TMembershipRoles,
TMembershipRolesInsert,
TMembershipRolesUpdate,
TMemberships,
TMembershipsInsert,
TMembershipsUpdate,
TNamespaces,
TNamespacesInsert,
TNamespacesUpdate,
TOidcConfigs,
TOidcConfigsInsert,
TOidcConfigsUpdate,
@@ -314,6 +326,9 @@ import {
TResourceMetadata,
TResourceMetadataInsert,
TResourceMetadataUpdate,
TRoles,
TRolesInsert,
TRolesUpdate,
TSamlConfigs,
TSamlConfigsInsert,
TSamlConfigsUpdate,
@@ -1316,5 +1331,19 @@ declare module "knex/types/tables" {
[TableName.PamResource]: KnexOriginal.CompositeTableType<TPamResources, TPamResourcesInsert, TPamResourcesUpdate>;
[TableName.PamAccount]: KnexOriginal.CompositeTableType<TPamAccounts, TPamAccountsInsert, TPamAccountsUpdate>;
[TableName.PamSession]: KnexOriginal.CompositeTableType<TPamSessions, TPamSessionsInsert, TPamSessionsUpdate>;
[TableName.Namespace]: KnexOriginal.CompositeTableType<TNamespaces, TNamespacesInsert, TNamespacesUpdate>;
[TableName.Membership]: KnexOriginal.CompositeTableType<TMemberships, TMembershipsInsert, TMembershipsUpdate>;
[TableName.MembershipRoles]: KnexOriginal.CompositeTableType<
TMembershipRoles,
TMembershipRolesInsert,
TMembershipRolesUpdate
>;
[TableName.Roles]: KnexOriginal.CompositeTableType<TRoles, TRolesInsert, TRolesUpdate>;
[TableName.AdditionalPrivilege]: KnexOriginal.CompositeTableType<
TAdditionalPrivileges,
TAdditionalPrivilegesInsert,
TAdditionalPrivilegesUpdate
>;
}
}

View File

@@ -0,0 +1,166 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
export async function up(knex: Knex): Promise<void> {
if (!(await knex.schema.hasTable(TableName.Namespace))) {
await knex.schema.createTable(TableName.Namespace, (t) => {
t.uuid("id").primary().defaultTo(knex.fn.uuid());
t.string("name").notNullable();
t.string("description");
t.uuid("orgId").notNullable();
t.foreign("orgId").references("id").inTable(TableName.Organization).onDelete("CASCADE");
t.timestamps(true, true, true);
});
await createOnUpdateTrigger(knex, TableName.Namespace);
}
if (!(await knex.schema.hasTable(TableName.Membership))) {
await knex.schema.createTable(TableName.Membership, (t) => {
t.uuid("id").primary().defaultTo(knex.fn.uuid());
t.string("scope", 24).notNullable();
t.uuid("actorUserId");
t.foreign("actorUserId").references("id").inTable(TableName.Users).onDelete("CASCADE");
t.uuid("actorIdentityId");
t.foreign("actorIdentityId").references("id").inTable(TableName.Identity).onDelete("CASCADE");
t.uuid("actorGroupId");
t.foreign("actorGroupId").references("id").inTable(TableName.Groups).onDelete("CASCADE");
t.uuid("scopeOrgId").notNullable();
t.foreign("scopeOrgId").references("id").inTable(TableName.Organization).onDelete("CASCADE");
t.string("scopeProjectId", 36);
t.foreign("scopeProjectId").references("id").inTable(TableName.Project).onDelete("CASCADE");
t.uuid("scopeNamespaceId");
t.foreign("scopeNamespaceId").references("id").inTable(TableName.Namespace).onDelete("CASCADE");
t.boolean("isActive");
t.string("status").defaultTo("invited");
t.string("inviteEmail");
t.datetime("lastInvitedAt");
t.datetime("lastLoginAuthMethod");
t.datetime("lastLoginTime");
t.specificType("projectFavorites", "text[]");
t.timestamps(true, true, true);
t.check(
`(:actorUserIdColumn: IS NOT NULL AND :actorIdentityIdColumn: IS NULL AND :actorGroupIdColumn: IS NULL) OR
(:actorIdentityIdColumn: IS NOT NULL AND :actorUserIdColumn: IS NULL AND :actorGroupIdColumn: IS NULL) OR
(:actorGroupIdColumn: IS NOT NULL AND :actorUserIdColumn: IS NULL AND :actorIdentityIdColumn: IS NULL)`,
{
actorUserIdColumn: "actorUserId",
actorIdentityIdColumn: "actorIdentityId",
actorGroupIdColumn: "actorGroupId"
},
"only_one_actor_type"
);
t.check(
`(:scopeColumn: = 'namespace' AND :scopeNamespaceIdColumn: IS NOT NULL) OR
(:scopeColumn: = 'project' AND :scopeProjectIdColumn: IS NOT NULL) OR
(:scopeColumn: = 'organization')
`,
{
scopeColumn: "scope",
scopeNamespaceIdColumn: "scopeNamespaceId",
scopeProjectIdColumn: "scopeProjectId"
},
"scope_matches_id"
);
});
await createOnUpdateTrigger(knex, TableName.Membership);
}
if (!(await knex.schema.hasTable(TableName.Role))) {
await knex.schema.createTable(TableName.Role, (t) => {
t.uuid("id").primary().defaultTo(knex.fn.uuid());
t.string("name").notNullable();
t.string("description");
t.string("slug").notNullable();
t.jsonb("permissions").notNullable();
t.uuid("orgId");
t.foreign("orgId").references("id").inTable(TableName.Organization).onDelete("CASCADE");
t.string("projectId", 36);
t.foreign("projectId").references("id").inTable(TableName.Project).onDelete("CASCADE");
t.uuid("namespaceId");
t.foreign("namespaceId").references("id").inTable(TableName.Namespace).onDelete("CASCADE");
t.check(
`(:orgIdColumn: IS NOT NULL AND :namespaceIdColumn: IS NULL AND :projectIdColumn: IS NULL) OR
(:namespaceIdColumn: IS NOT NULL AND :orgIdColumn: IS NULL AND :projectIdColumn: IS NULL) OR
(:projectIdColumn: IS NOT NULL AND :orgIdColumn: IS NULL AND :namespaceIdColumn: IS NULL)`,
{
orgIdColumn: "orgId",
namespaceIdColumn: "namespaceId",
projectIdColumn: "projectId"
},
"only_one_scope_id"
);
t.timestamps(true, true, true);
});
await createOnUpdateTrigger(knex, TableName.Role);
}
if (!(await knex.schema.hasTable(TableName.MembershipRole))) {
await knex.schema.createTable(TableName.MembershipRole, (t) => {
t.uuid("id").primary().defaultTo(knex.fn.uuid());
t.string("role").notNullable();
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.uuid("customRoleId");
t.foreign("customRoleId").references("id").inTable(TableName.Role);
t.uuid("membershipId").notNullable();
t.foreign("membershipId").references("id").inTable(TableName.Membership).onDelete("CASCADE");
t.timestamps(true, true, true);
});
await createOnUpdateTrigger(knex, TableName.MembershipRole);
}
if (!(await knex.schema.hasTable(TableName.AdditionalPrivilege))) {
await knex.schema.createTable(TableName.AdditionalPrivilege, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.string("name", 60).notNullable();
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.jsonb("permissions").notNullable();
t.uuid("membershipId").notNullable();
t.foreign("membershipId").references("id").inTable(TableName.Membership).onDelete("CASCADE");
t.timestamps(true, true, true);
});
await createOnUpdateTrigger(knex, TableName.AdditionalPrivilege);
}
}
export async function down(knex: Knex): Promise<void> {
await dropOnUpdateTrigger(knex, TableName.AdditionalPrivilege);
await knex.schema.dropTableIfExists(TableName.AdditionalPrivilege);
await dropOnUpdateTrigger(knex, TableName.MembershipRole);
await knex.schema.dropTableIfExists(TableName.MembershipRole);
await dropOnUpdateTrigger(knex, TableName.Membership);
await knex.schema.dropTableIfExists(TableName.Membership);
await dropOnUpdateTrigger(knex, TableName.Role);
await knex.schema.dropTableIfExists(TableName.Role);
await dropOnUpdateTrigger(knex, TableName.Namespace);
await knex.schema.dropTableIfExists(TableName.Namespace);
}

View File

@@ -0,0 +1,26 @@
// 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 AdditionalPrivilegesSchema = z.object({
id: z.string().uuid(),
name: z.string(),
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(),
permissions: z.unknown(),
membershipId: z.string().uuid(),
createdAt: z.date(),
updatedAt: z.date()
});
export type TAdditionalPrivileges = z.infer<typeof AdditionalPrivilegesSchema>;
export type TAdditionalPrivilegesInsert = Omit<z.input<typeof AdditionalPrivilegesSchema>, TImmutableDBKeys>;
export type TAdditionalPrivilegesUpdate = Partial<Omit<z.input<typeof AdditionalPrivilegesSchema>, TImmutableDBKeys>>;

View File

@@ -3,6 +3,7 @@ export * from "./access-approval-policies-approvers";
export * from "./access-approval-policies-bypassers";
export * from "./access-approval-requests";
export * from "./access-approval-requests-reviewers";
export * from "./additional-privileges";
export * from "./api-keys";
export * from "./app-connections";
export * from "./audit-log-streams";
@@ -73,8 +74,11 @@ export * from "./kms-keys";
export * from "./kms-root-config";
export * from "./ldap-configs";
export * from "./ldap-group-maps";
export * from "./membership-roles";
export * from "./memberships";
export * from "./microsoft-teams-integrations";
export * from "./models";
export * from "./namespaces";
export * from "./oidc-configs";
export * from "./org-bots";
export * from "./org-gateway-config";
@@ -108,6 +112,7 @@ export * from "./projects";
export * from "./rate-limit";
export * from "./relays";
export * from "./resource-metadata";
export * from "./roles";
export * from "./saml-configs";
export * from "./scim-tokens";
export * from "./secret-approval-policies";

View File

@@ -0,0 +1,26 @@
// 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 MembershipRolesSchema = z.object({
id: z.string().uuid(),
role: z.string(),
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(),
customRoleId: z.string().uuid().nullable().optional(),
membershipId: z.string().uuid(),
createdAt: z.date(),
updatedAt: z.date()
});
export type TMembershipRoles = z.infer<typeof MembershipRolesSchema>;
export type TMembershipRolesInsert = Omit<z.input<typeof MembershipRolesSchema>, TImmutableDBKeys>;
export type TMembershipRolesUpdate = Partial<Omit<z.input<typeof MembershipRolesSchema>, TImmutableDBKeys>>;

View File

@@ -0,0 +1,32 @@
// 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 MembershipsSchema = z.object({
id: z.string().uuid(),
scope: z.string(),
actorUserId: z.string().uuid().nullable().optional(),
actorIdentityId: z.string().uuid().nullable().optional(),
actorGroupId: z.string().uuid().nullable().optional(),
scopeOrgId: z.string().uuid(),
scopeProjectId: z.string().nullable().optional(),
scopeNamespaceId: z.string().uuid().nullable().optional(),
isActive: z.boolean().nullable().optional(),
status: z.string().default("invited").nullable().optional(),
inviteEmail: z.string().nullable().optional(),
lastInvitedAt: z.date().nullable().optional(),
lastLoginAuthMethod: z.date().nullable().optional(),
lastLoginTime: z.date().nullable().optional(),
projectFavorites: z.string().array().nullable().optional(),
createdAt: z.date(),
updatedAt: z.date()
});
export type TMemberships = z.infer<typeof MembershipsSchema>;
export type TMembershipsInsert = Omit<z.input<typeof MembershipsSchema>, TImmutableDBKeys>;
export type TMembershipsUpdate = Partial<Omit<z.input<typeof MembershipsSchema>, TImmutableDBKeys>>;

View File

@@ -178,6 +178,14 @@ export enum TableName {
SecretScanningScan = "secret_scanning_scans",
SecretScanningFinding = "secret_scanning_findings",
SecretScanningConfig = "secret_scanning_configs",
Membership = "memberships",
MembershipRole = "membership_roles",
Role = "roles",
AdditionalPrivilege = "additional_privileges",
Namespace = "namespaces",
// reminders
Reminder = "reminders",
ReminderRecipient = "reminders_recipients",

View File

@@ -0,0 +1,21 @@
// 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 NamespacesSchema = z.object({
id: z.string().uuid(),
name: z.string(),
description: z.string().nullable().optional(),
orgId: z.string().uuid(),
createdAt: z.date(),
updatedAt: z.date()
});
export type TNamespaces = z.infer<typeof NamespacesSchema>;
export type TNamespacesInsert = Omit<z.input<typeof NamespacesSchema>, TImmutableDBKeys>;
export type TNamespacesUpdate = Partial<Omit<z.input<typeof NamespacesSchema>, TImmutableDBKeys>>;

View File

@@ -0,0 +1,25 @@
// 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 RolesSchema = z.object({
id: z.string().uuid(),
name: z.string(),
description: z.string().nullable().optional(),
slug: z.string(),
permissions: z.unknown(),
orgId: z.string().uuid().nullable().optional(),
projectId: z.string().nullable().optional(),
namespaceId: z.string().uuid().nullable().optional(),
createdAt: z.date(),
updatedAt: z.date()
});
export type TRoles = z.infer<typeof RolesSchema>;
export type TRolesInsert = Omit<z.input<typeof RolesSchema>, TImmutableDBKeys>;
export type TRolesUpdate = Partial<Omit<z.input<typeof RolesSchema>, TImmutableDBKeys>>;