feat: switched up all dal except policy ones

This commit is contained in:
=
2025-10-10 00:09:45 +05:30
parent 4d68d48d89
commit 0ddf98ec15
37 changed files with 1022 additions and 895 deletions
@@ -36,7 +36,7 @@ export async function up(knex: Knex): Promise<void> {
t.uuid("scopeNamespaceId"); t.uuid("scopeNamespaceId");
t.foreign("scopeNamespaceId").references("id").inTable(TableName.Namespace).onDelete("CASCADE"); t.foreign("scopeNamespaceId").references("id").inTable(TableName.Namespace).onDelete("CASCADE");
t.boolean("isActive"); t.boolean("isActive").defaultTo(true).notNullable();
t.string("status").defaultTo("invited"); t.string("status").defaultTo("invited");
t.string("inviteEmail"); t.string("inviteEmail");
t.datetime("lastInvitedAt"); t.datetime("lastInvitedAt");
+20 -2
View File
@@ -261,7 +261,16 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
}), }),
response: { response: {
200: z.object({ 200: z.object({
memberships: OrgMembershipsSchema.array(), memberships: z
.object({
id: z.string(),
roles: z
.object({
role: z.string()
})
.array()
})
.array(),
permissions: z.any().array() permissions: z.any().array()
}) })
} }
@@ -274,7 +283,16 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
req.permission.authMethod, req.permission.authMethod,
req.permission.orgId req.permission.orgId
); );
return { permissions, memberships }; return {
permissions,
memberships: memberships.map((el) => ({
...el,
role: el.roles[0].customRoleSlug || el.roles[0].role,
orgId: el.scopeOrgId,
status: el.status || "",
isActive: el.isActive || true
}))
};
} }
}); });
}; };
@@ -1,7 +1,7 @@
import { packRules } from "@casl/ability/extra"; import { packRules } from "@casl/ability/extra";
import { z } from "zod"; import { z } from "zod";
import { ProjectMembershipRole, ProjectRolesSchema, AccessScope } from "@app/db/schemas"; import { AccessScope, ProjectMembershipRole, ProjectRolesSchema } from "@app/db/schemas";
import { EventType } from "@app/ee/services/audit-log/audit-log-types"; import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { checkForInvalidPermissionCombination } from "@app/ee/services/permission/permission-fns"; import { checkForInvalidPermissionCombination } from "@app/ee/services/permission/permission-fns";
import { ProjectPermissionV2Schema } from "@app/ee/services/permission/project-permission"; import { ProjectPermissionV2Schema } from "@app/ee/services/permission/project-permission";
@@ -238,17 +238,17 @@ export interface TAccessApprovalRequestDALFactory extends Omit<TOrmify<TableName
export const accessApprovalRequestDALFactory = (db: TDbClient): TAccessApprovalRequestDALFactory => { export const accessApprovalRequestDALFactory = (db: TDbClient): TAccessApprovalRequestDALFactory => {
const accessApprovalRequestOrm = ormify(db, TableName.AccessApprovalRequest); const accessApprovalRequestOrm = ormify(db, TableName.AccessApprovalRequest);
// TODO(simp): check this from begginging
const findRequestsWithPrivilegeByPolicyIds: TAccessApprovalRequestDALFactory["findRequestsWithPrivilegeByPolicyIds"] = const findRequestsWithPrivilegeByPolicyIds: TAccessApprovalRequestDALFactory["findRequestsWithPrivilegeByPolicyIds"] =
async (policyIds) => { async (policyIds) => {
try { try {
const docs = await db const docs = await db
.replicaNode()(TableName.AccessApprovalRequest) .replicaNode()(TableName.AccessApprovalRequest)
.whereIn(`${TableName.AccessApprovalRequest}.policyId`, policyIds) .whereIn(`${TableName.AccessApprovalRequest}.policyId`, policyIds)
.leftJoin( .leftJoin(
TableName.ProjectUserAdditionalPrivilege, TableName.AdditionalPrivilege,
`${TableName.AccessApprovalRequest}.privilegeId`, `${TableName.AccessApprovalRequest}.privilegeId`,
`${TableName.ProjectUserAdditionalPrivilege}.id` `${TableName.AdditionalPrivilege}.id`
) )
.leftJoin( .leftJoin(
TableName.AccessApprovalPolicy, TableName.AccessApprovalPolicy,
@@ -276,7 +276,6 @@ export const accessApprovalRequestDALFactory = (db: TDbClient): TAccessApprovalR
`${TableName.UserGroupMembership}.groupId` `${TableName.UserGroupMembership}.groupId`
) )
.leftJoin(TableName.Users, `${TableName.UserGroupMembership}.userId`, `${TableName.Users}.id`) .leftJoin(TableName.Users, `${TableName.UserGroupMembership}.userId`, `${TableName.Users}.id`)
.leftJoin( .leftJoin(
TableName.AccessApprovalPolicyBypasser, TableName.AccessApprovalPolicyBypasser,
`${TableName.AccessApprovalPolicy}.id`, `${TableName.AccessApprovalPolicy}.id`,
+18 -9
View File
@@ -1,7 +1,7 @@
import { Knex } from "knex"; import { Knex } from "knex";
import { TDbClient } from "@app/db"; import { TDbClient } from "@app/db";
import { TableName, TGroups } from "@app/db/schemas"; import { AccessScope, TableName, TGroups } from "@app/db/schemas";
import { DatabaseError } from "@app/lib/errors"; import { DatabaseError } from "@app/lib/errors";
import { buildFindFilter, ormify, selectAllTableCols, TFindFilter, TFindOpt } from "@app/lib/knex"; import { buildFindFilter, ormify, selectAllTableCols, TFindFilter, TFindOpt } from "@app/lib/knex";
@@ -81,9 +81,11 @@ export const groupDALFactory = (db: TDbClient) => {
}) => { }) => {
try { try {
const query = db const query = db
.replicaNode()(TableName.OrgMembership) .replicaNode()(TableName.Membership)
.where(`${TableName.OrgMembership}.orgId`, orgId) .where(`${TableName.Membership}.scopeOrgId`, orgId)
.join(TableName.Users, `${TableName.OrgMembership}.userId`, `${TableName.Users}.id`) .where(`${TableName.Membership}.scope`, AccessScope.Organization)
.whereNotNull(`${TableName.Membership}.actorUserId`)
.join(TableName.Users, `${TableName.Membership}.actorUserId`, `${TableName.Users}.id`)
.leftJoin(TableName.UserGroupMembership, (bd) => { .leftJoin(TableName.UserGroupMembership, (bd) => {
bd.on(`${TableName.UserGroupMembership}.userId`, "=", `${TableName.Users}.id`).andOn( bd.on(`${TableName.UserGroupMembership}.userId`, "=", `${TableName.Users}.id`).andOn(
`${TableName.UserGroupMembership}.groupId`, `${TableName.UserGroupMembership}.groupId`,
@@ -92,7 +94,7 @@ export const groupDALFactory = (db: TDbClient) => {
); );
}) })
.select( .select(
db.ref("id").withSchema(TableName.OrgMembership), db.ref("id").withSchema(TableName.Membership),
db.ref("groupId").withSchema(TableName.UserGroupMembership), db.ref("groupId").withSchema(TableName.UserGroupMembership),
db.ref("createdAt").withSchema(TableName.UserGroupMembership).as("joinedGroupAt"), db.ref("createdAt").withSchema(TableName.UserGroupMembership).as("joinedGroupAt"),
db.ref("email").withSchema(TableName.Users), db.ref("email").withSchema(TableName.Users),
@@ -160,8 +162,10 @@ export const groupDALFactory = (db: TDbClient) => {
const findGroupsByProjectId = async (projectId: string, tx?: Knex) => { const findGroupsByProjectId = async (projectId: string, tx?: Knex) => {
try { try {
const docs = await (tx || db.replicaNode())(TableName.Groups) const docs = await (tx || db.replicaNode())(TableName.Groups)
.join(TableName.GroupProjectMembership, `${TableName.Groups}.id`, `${TableName.GroupProjectMembership}.groupId`) .join(TableName.Membership, `${TableName.Membership}.actorGroupId`, `${TableName.Groups}.id`)
.where(`${TableName.GroupProjectMembership}.projectId`, projectId) .where(`${TableName.Membership}.scopeProjectId`, projectId)
.where(`${TableName.Membership}.scope`, AccessScope.Project)
.whereNotNull(`${TableName.Membership}.actorGroupId`)
.select(selectAllTableCols(TableName.Groups)); .select(selectAllTableCols(TableName.Groups));
return docs; return docs;
} catch (error) { } catch (error) {
@@ -172,11 +176,16 @@ export const groupDALFactory = (db: TDbClient) => {
const findById = async (id: string, tx?: Knex) => { const findById = async (id: string, tx?: Knex) => {
try { try {
const doc = await (tx || db.replicaNode())(TableName.Groups) const doc = await (tx || db.replicaNode())(TableName.Groups)
.leftJoin(TableName.OrgRoles, `${TableName.Groups}.roleId`, `${TableName.OrgRoles}.id`) .join(TableName.Membership, `${TableName.Membership}.actorGroupId`, `${TableName.Groups}.id`)
.join(TableName.MembershipRole, `${TableName.MembershipRole}.membershipId`, `${TableName.Membership}.id`)
.leftJoin(TableName.Role, `${TableName.MembershipRole}.customRoleId`, `${TableName.Role}.id`)
.where(`${TableName.Groups}.id`, id) .where(`${TableName.Groups}.id`, id)
.where(`${TableName.Membership}.scope`, AccessScope.Organization)
.select( .select(
selectAllTableCols(TableName.Groups), selectAllTableCols(TableName.Groups),
db.ref("slug").as("customRoleSlug").withSchema(TableName.OrgRoles) db.ref("slug").as("customRoleSlug").withSchema(TableName.Role),
db.ref("customRoleId").as("roleId").withSchema(TableName.MembershipRole),
db.ref("role").withSchema(TableName.MembershipRole)
) )
.first(); .first();
@@ -1,7 +1,7 @@
import { Knex } from "knex"; import { Knex } from "knex";
import { TDbClient } from "@app/db"; import { TDbClient } from "@app/db";
import { TableName, TUserEncryptionKeys } from "@app/db/schemas"; import { AccessScope, TableName, TUserEncryptionKeys } from "@app/db/schemas";
import { DatabaseError } from "@app/lib/errors"; import { DatabaseError } from "@app/lib/errors";
import { ormify } from "@app/lib/knex"; import { ormify } from "@app/lib/knex";
@@ -18,21 +18,19 @@ export const userGroupMembershipDALFactory = (db: TDbClient) => {
*/ */
const filterProjectsByUserMembership = async (userId: string, groupId: string, projectIds: string[], tx?: Knex) => { const filterProjectsByUserMembership = async (userId: string, groupId: string, projectIds: string[], tx?: Knex) => {
try { try {
const userProjectMemberships: string[] = await (tx || db.replicaNode())(TableName.ProjectMembership) const userProjectMemberships: string[] = await (tx || db.replicaNode())(TableName.Membership)
.where(`${TableName.ProjectMembership}.userId`, userId) .where(`${TableName.Membership}.actorUserId`, userId)
.whereIn(`${TableName.ProjectMembership}.projectId`, projectIds) .where(`${TableName.Membership}.scope`, AccessScope.Project)
.pluck(`${TableName.ProjectMembership}.projectId`); .whereIn(`${TableName.Membership}.scopeProjectId`, projectIds)
.pluck(`${TableName.Membership}.scopeProjectId`);
const userGroupMemberships: string[] = await (tx || db.replicaNode())(TableName.UserGroupMembership) const userGroupMemberships: string[] = await (tx || db.replicaNode())(TableName.UserGroupMembership)
.where(`${TableName.UserGroupMembership}.userId`, userId) .where(`${TableName.UserGroupMembership}.userId`, userId)
.whereNot(`${TableName.UserGroupMembership}.groupId`, groupId) .whereNot(`${TableName.UserGroupMembership}.groupId`, groupId)
.join( .join(TableName.Membership, `${TableName.UserGroupMembership}.groupId`, `${TableName.Membership}.actorGroupId`)
TableName.GroupProjectMembership, .where(`${TableName.Membership}.scope`, AccessScope.Project)
`${TableName.UserGroupMembership}.groupId`, .whereIn(`${TableName.Membership}.scopeProjectId`, projectIds)
`${TableName.GroupProjectMembership}.groupId` .pluck(`${TableName.Membership}.scopeProjectId`);
)
.whereIn(`${TableName.GroupProjectMembership}.projectId`, projectIds)
.pluck(`${TableName.GroupProjectMembership}.projectId`);
return new Set(userProjectMemberships.concat(userGroupMemberships)); return new Set(userProjectMemberships.concat(userGroupMemberships));
} catch (error) { } catch (error) {
@@ -44,13 +42,10 @@ export const userGroupMembershipDALFactory = (db: TDbClient) => {
const findUserGroupMembershipsInProject = async (usernames: string[], projectId: string, tx?: Knex) => { const findUserGroupMembershipsInProject = async (usernames: string[], projectId: string, tx?: Knex) => {
try { try {
const usernameDocs: string[] = await (tx || db.replicaNode())(TableName.UserGroupMembership) const usernameDocs: string[] = await (tx || db.replicaNode())(TableName.UserGroupMembership)
.join( .join(TableName.Membership, `${TableName.UserGroupMembership}.groupId`, `${TableName.Membership}.actorGroupId`)
TableName.GroupProjectMembership, .where(`${TableName.Membership}.scope`, AccessScope.Project)
`${TableName.UserGroupMembership}.groupId`,
`${TableName.GroupProjectMembership}.groupId`
)
.join(TableName.Users, `${TableName.UserGroupMembership}.userId`, `${TableName.Users}.id`) .join(TableName.Users, `${TableName.UserGroupMembership}.userId`, `${TableName.Users}.id`)
.where(`${TableName.GroupProjectMembership}.projectId`, projectId) .where(`${TableName.Membership}.scopeProjectId`, projectId)
.whereIn(`${TableName.Users}.username`, usernames) .whereIn(`${TableName.Users}.username`, usernames)
.pluck(`${TableName.Users}.id`); .pluck(`${TableName.Users}.id`);
@@ -73,24 +68,25 @@ export const userGroupMembershipDALFactory = (db: TDbClient) => {
try { try {
// get list of groups in the project with id [projectId] // get list of groups in the project with id [projectId]
// that that are not the group with id [groupId] // that that are not the group with id [groupId]
const groups: string[] = await (tx || db.replicaNode())(TableName.GroupProjectMembership) const groups: string[] = await (tx || db.replicaNode())(TableName.Membership)
.where(`${TableName.GroupProjectMembership}.projectId`, projectId) .where(`${TableName.Membership}.scopeProjectId`, projectId)
.whereNot(`${TableName.GroupProjectMembership}.groupId`, groupId) .whereNot(`${TableName.Membership}.actorGroupId`, groupId)
.pluck(`${TableName.GroupProjectMembership}.groupId`); .pluck(`${TableName.Membership}.groupId`);
// main query // main query
const members = await (tx || db.replicaNode())(TableName.UserGroupMembership) const members = await (tx || db.replicaNode())(TableName.UserGroupMembership)
.where(`${TableName.UserGroupMembership}.groupId`, groupId) .where(`${TableName.UserGroupMembership}.groupId`, groupId)
.where(`${TableName.UserGroupMembership}.isPending`, false) .where(`${TableName.UserGroupMembership}.isPending`, false)
.join(TableName.Users, `${TableName.UserGroupMembership}.userId`, `${TableName.Users}.id`) .join(TableName.Users, `${TableName.UserGroupMembership}.userId`, `${TableName.Users}.id`)
.leftJoin(TableName.ProjectMembership, (bd) => { .leftJoin(TableName.Membership, (bd) => {
bd.on(`${TableName.Users}.id`, "=", `${TableName.ProjectMembership}.userId`).andOn( bd.on(`${TableName.Users}.id`, "=", `${TableName.Membership}.actorUserId`).andOn(
`${TableName.ProjectMembership}.projectId`, `${TableName.Membership}.scopeProjectId`,
"=", "=",
db.raw("?", [projectId]) db.raw("?", [projectId])
); );
}) })
.whereNull(`${TableName.ProjectMembership}.userId`) .whereNull(`${TableName.Membership}.actorUserId`)
.where(`${TableName.Membership}.scope`, AccessScope.Project)
.leftJoin<TUserEncryptionKeys>( .leftJoin<TUserEncryptionKeys>(
TableName.UserEncryptionKey, TableName.UserEncryptionKey,
`${TableName.UserEncryptionKey}.userId`, `${TableName.UserEncryptionKey}.userId`,
@@ -166,15 +162,17 @@ export const userGroupMembershipDALFactory = (db: TDbClient) => {
const docs = await db const docs = await db
.replicaNode()(TableName.UserGroupMembership) .replicaNode()(TableName.UserGroupMembership)
.join(TableName.Groups, `${TableName.UserGroupMembership}.groupId`, `${TableName.Groups}.id`) .join(TableName.Groups, `${TableName.UserGroupMembership}.groupId`, `${TableName.Groups}.id`)
.join(TableName.OrgMembership, `${TableName.UserGroupMembership}.userId`, `${TableName.OrgMembership}.userId`) .join(TableName.Membership, `${TableName.UserGroupMembership}.userId`, `${TableName.Membership}.actorUserId`)
.join(TableName.Users, `${TableName.UserGroupMembership}.userId`, `${TableName.Users}.id`) .join(TableName.Users, `${TableName.UserGroupMembership}.userId`, `${TableName.Users}.id`)
.where(`${TableName.UserGroupMembership}.userId`, userId) .where(`${TableName.UserGroupMembership}.userId`, userId)
.where(`${TableName.Membership}.scope`, AccessScope.Organization)
.where(`${TableName.Membership}.scopeOrgId`, orgId)
.where(`${TableName.Groups}.orgId`, orgId) .where(`${TableName.Groups}.orgId`, orgId)
.select( .select(
db.ref("id").withSchema(TableName.UserGroupMembership), db.ref("id").withSchema(TableName.UserGroupMembership),
db.ref("groupId").withSchema(TableName.UserGroupMembership), db.ref("groupId").withSchema(TableName.UserGroupMembership),
db.ref("name").withSchema(TableName.Groups).as("groupName"), db.ref("name").withSchema(TableName.Groups).as("groupName"),
db.ref("id").withSchema(TableName.OrgMembership).as("orgMembershipId"), db.ref("id").withSchema(TableName.Membership).as("orgMembershipId"),
db.ref("firstName").withSchema(TableName.Users).as("firstName"), db.ref("firstName").withSchema(TableName.Users).as("firstName"),
db.ref("lastName").withSchema(TableName.Users).as("lastName"), db.ref("lastName").withSchema(TableName.Users).as("lastName"),
db.ref("slug").withSchema(TableName.Groups).as("groupSlug") db.ref("slug").withSchema(TableName.Groups).as("groupSlug")
@@ -191,15 +189,17 @@ export const userGroupMembershipDALFactory = (db: TDbClient) => {
const docs = await db const docs = await db
.replicaNode()(TableName.UserGroupMembership) .replicaNode()(TableName.UserGroupMembership)
.join(TableName.Groups, `${TableName.UserGroupMembership}.groupId`, `${TableName.Groups}.id`) .join(TableName.Groups, `${TableName.UserGroupMembership}.groupId`, `${TableName.Groups}.id`)
.join(TableName.OrgMembership, `${TableName.UserGroupMembership}.userId`, `${TableName.OrgMembership}.userId`) .join(TableName.Membership, `${TableName.UserGroupMembership}.userId`, `${TableName.Membership}.actorGroupId`)
.join(TableName.Users, `${TableName.UserGroupMembership}.userId`, `${TableName.Users}.id`) .join(TableName.Users, `${TableName.UserGroupMembership}.userId`, `${TableName.Users}.id`)
.where(`${TableName.Groups}.id`, groupId) .where(`${TableName.Groups}.id`, groupId)
.where(`${TableName.Membership}.scope`, AccessScope.Organization)
.where(`${TableName.Membership}.scopeOrgId`, orgId)
.where(`${TableName.Groups}.orgId`, orgId) .where(`${TableName.Groups}.orgId`, orgId)
.select( .select(
db.ref("id").withSchema(TableName.UserGroupMembership), db.ref("id").withSchema(TableName.UserGroupMembership),
db.ref("groupId").withSchema(TableName.UserGroupMembership), db.ref("groupId").withSchema(TableName.UserGroupMembership),
db.ref("name").withSchema(TableName.Groups).as("groupName"), db.ref("name").withSchema(TableName.Groups).as("groupName"),
db.ref("id").withSchema(TableName.OrgMembership).as("orgMembershipId"), db.ref("id").withSchema(TableName.Membership).as("orgMembershipId"),
db.ref("firstName").withSchema(TableName.Users).as("firstName"), db.ref("firstName").withSchema(TableName.Users).as("firstName"),
db.ref("lastName").withSchema(TableName.Users).as("lastName") db.ref("lastName").withSchema(TableName.Users).as("lastName")
); );
+14 -11
View File
@@ -1,7 +1,7 @@
import { Knex } from "knex"; import { Knex } from "knex";
import { TDbClient } from "@app/db"; import { TDbClient } from "@app/db";
import { OrgMembershipStatus, TableName } from "@app/db/schemas"; import { AccessScope, OrgMembershipStatus, TableName } from "@app/db/schemas";
import { DatabaseError } from "@app/lib/errors"; import { DatabaseError } from "@app/lib/errors";
export type TLicenseDALFactory = ReturnType<typeof licenseDALFactory>; export type TLicenseDALFactory = ReturnType<typeof licenseDALFactory>;
@@ -9,14 +9,14 @@ export type TLicenseDALFactory = ReturnType<typeof licenseDALFactory>;
export const licenseDALFactory = (db: TDbClient) => { export const licenseDALFactory = (db: TDbClient) => {
const countOfOrgMembers = async (orgId: string | null, tx?: Knex) => { const countOfOrgMembers = async (orgId: string | null, tx?: Knex) => {
try { try {
const doc = await (tx || db.replicaNode())(TableName.OrgMembership) const doc = await (tx || db.replicaNode())(TableName.Membership)
.where({ status: OrgMembershipStatus.Accepted }) .where({ status: OrgMembershipStatus.Accepted, scope: AccessScope.Organization })
.andWhere((bd) => { .andWhere((bd) => {
if (orgId) { if (orgId) {
void bd.where({ orgId }); void bd.where(`${TableName.Membership}.scopeOrgId`, orgId);
} }
}) })
.join(TableName.Users, `${TableName.OrgMembership}.userId`, `${TableName.Users}.id`) .join(TableName.Users, `${TableName.Membership}.actorUserId`, `${TableName.Users}.id`)
.where(`${TableName.Users}.isGhost`, false) .where(`${TableName.Users}.isGhost`, false)
.count(); .count();
return Number(doc?.[0]?.count ?? 0); return Number(doc?.[0]?.count ?? 0);
@@ -28,24 +28,27 @@ export const licenseDALFactory = (db: TDbClient) => {
const countOrgUsersAndIdentities = async (orgId: string | null, tx?: Knex) => { const countOrgUsersAndIdentities = async (orgId: string | null, tx?: Knex) => {
try { try {
// count org users // count org users
const userDoc = await (tx || db.replicaNode())(TableName.OrgMembership) const userDoc = await (tx || db.replicaNode())(TableName.Membership)
.where({ status: OrgMembershipStatus.Accepted }) .where({ status: OrgMembershipStatus.Accepted, scope: AccessScope.Organization })
.whereNotNull(`${TableName.Membership}.actorUserId`)
.andWhere((bd) => { .andWhere((bd) => {
if (orgId) { if (orgId) {
void bd.where({ orgId }); void bd.where(`${TableName.Membership}.scopeOrgId`, orgId);
} }
}) })
.join(TableName.Users, `${TableName.OrgMembership}.userId`, `${TableName.Users}.id`) .join(TableName.Users, `${TableName.Membership}.userId`, `${TableName.Users}.id`)
.where(`${TableName.Users}.isGhost`, false) .where(`${TableName.Users}.isGhost`, false)
.count(); .count();
const userCount = Number(userDoc?.[0].count); const userCount = Number(userDoc?.[0].count);
// count org identities // count org identities
const identityDoc = await (tx || db.replicaNode())(TableName.IdentityOrgMembership) const identityDoc = await (tx || db.replicaNode())(TableName.Membership)
.where({ status: OrgMembershipStatus.Accepted, scope: AccessScope.Organization })
.whereNotNull(`${TableName.Membership}.actorIdentityId`)
.where((bd) => { .where((bd) => {
if (orgId) { if (orgId) {
void bd.where({ orgId }); void bd.where(`${TableName.Membership}.scopeOrgId`, orgId);
} }
}) })
.count(); .count();
@@ -1,10 +0,0 @@
import { TDbClient } from "@app/db";
import { TableName } from "@app/db/schemas";
import { ormify, TOrmify } from "@app/lib/knex";
export type TProjectUserAdditionalPrivilegeDALFactory = TOrmify<TableName.ProjectUserAdditionalPrivilege>;
export const projectUserAdditionalPrivilegeDALFactory = (db: TDbClient): TProjectUserAdditionalPrivilegeDALFactory => {
const orm = ormify(db, TableName.ProjectUserAdditionalPrivilege);
return orm;
};
-2
View File
@@ -252,7 +252,6 @@ import { offlineUsageReportServiceFactory } from "@app/services/offline-usage-re
import { incidentContactDALFactory } from "@app/services/org/incident-contacts-dal"; import { incidentContactDALFactory } from "@app/services/org/incident-contacts-dal";
import { orgBotDALFactory } from "@app/services/org/org-bot-dal"; import { orgBotDALFactory } from "@app/services/org/org-bot-dal";
import { orgDALFactory } from "@app/services/org/org-dal"; import { orgDALFactory } from "@app/services/org/org-dal";
import { orgRoleDALFactory } from "@app/services/org/org-role-dal";
import { orgRoleServiceFactory } from "@app/services/org/org-role-service"; import { orgRoleServiceFactory } from "@app/services/org/org-role-service";
import { orgServiceFactory } from "@app/services/org/org-service"; import { orgServiceFactory } from "@app/services/org/org-service";
import { orgAdminServiceFactory } from "@app/services/org-admin/org-admin-service"; import { orgAdminServiceFactory } from "@app/services/org-admin/org-admin-service";
@@ -394,7 +393,6 @@ export const registerRoutes = async (
const orgMembershipDAL = orgMembershipDALFactory(db); const orgMembershipDAL = orgMembershipDALFactory(db);
const orgBotDAL = orgBotDALFactory(db); const orgBotDAL = orgBotDALFactory(db);
const incidentContactDAL = incidentContactDALFactory(db); const incidentContactDAL = incidentContactDALFactory(db);
const orgRoleDAL = orgRoleDALFactory(db);
const rateLimitDAL = rateLimitDALFactory(db); const rateLimitDAL = rateLimitDALFactory(db);
const apiKeyDAL = apiKeyDALFactory(db); const apiKeyDAL = apiKeyDALFactory(db);
@@ -1,6 +1,7 @@
import { z } from "zod"; import { z } from "zod";
import { import {
AccessScope,
OrgMembershipsSchema, OrgMembershipsSchema,
ProjectMembershipsSchema, ProjectMembershipsSchema,
ProjectUserMembershipRolesSchema, ProjectUserMembershipRolesSchema,
@@ -66,14 +67,23 @@ export const registerDeprecatedProjectMembershipRouter = async (server: FastifyZ
}, },
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => { handler: async (req) => {
const memberships = await server.services.projectMembership.getProjectMemberships({ const { data: memberships } = await server.services.membershipUser.listMemberships({
actorId: req.permission.id, permission: req.permission,
actor: req.permission.type, scopeData: {
actorAuthMethod: req.permission.authMethod, scope: AccessScope.Project,
actorOrgId: req.permission.orgId, orgId: req.permission.orgId,
projectId: req.params.workspaceId projectId: req.params.workspaceId
},
data: {}
}); });
return { memberships };
return {
memberships: memberships.map((el) => ({
...el,
userId: el.actorUserId as string,
projectId: req.params.workspaceId
}))
};
} }
}); });
@@ -124,15 +134,30 @@ export const registerDeprecatedProjectMembershipRouter = async (server: FastifyZ
}, },
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => { handler: async (req) => {
const membership = await server.services.projectMembership.getProjectMembershipById({ const { userId } = await server.services.convertor.userMembershipIdToUserId(
actorId: req.permission.id, req.params.membershipId,
actor: req.permission.type, AccessScope.Project,
actorAuthMethod: req.permission.authMethod, req.permission.orgId
actorOrgId: req.permission.orgId, );
projectId: req.params.workspaceId, const membership = await server.services.membershipUser.getMembershipByUserId({
id: req.params.membershipId permission: req.permission,
scopeData: {
scope: AccessScope.Project,
orgId: req.permission.orgId,
projectId: req.params.workspaceId
},
selector: {
userId
}
}); });
return { membership };
return {
membership: {
...membership,
userId,
projectId: req.params.workspacId
}
};
} }
}); });
@@ -241,8 +266,8 @@ export const registerDeprecatedProjectMembershipRouter = async (server: FastifyZ
...req.auditLogInfo, ...req.auditLogInfo,
event: { event: {
type: EventType.ADD_BATCH_PROJECT_MEMBER, type: EventType.ADD_BATCH_PROJECT_MEMBER,
metadata: data.map(({ userId }) => ({ metadata: data.map(({ actorUserId }) => ({
userId: userId || "", userId: actorUserId || "",
email: "" email: ""
})) }))
} }
@@ -300,30 +325,28 @@ export const registerDeprecatedProjectMembershipRouter = async (server: FastifyZ
}, },
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => { handler: async (req) => {
const roles = await server.services.projectMembership.updateProjectMembership({ const { userId } = await server.services.convertor.userMembershipIdToUserId(
actorId: req.permission.id, req.params.membershipId,
actor: req.permission.type, AccessScope.Project,
actorAuthMethod: req.permission.authMethod, req.permission.orgId
actorOrgId: req.permission.orgId, );
projectId: req.params.workspaceId,
membershipId: req.params.membershipId, const { membership } = await server.services.membershipUser.updateMembership({
roles: req.body.roles permission: req.permission,
scopeData: {
scope: AccessScope.Project,
orgId: req.permission.orgId,
projectId: req.params.projectId
},
selector: {
userId
},
data: {
roles: req.body.roles
}
}); });
// await server.services.auditLog.createAuditLog({ return { roles: membership.roles.map((el) => ({ ...el, projectMembershipId: req.params.membershipId })) };
// ...req.auditLogInfo,
// projectId: req.params.workspaceId,
// event: {
// type: EventType.UPDATE_USER_WORKSPACE_ROLE,
// metadata: {
// userId: membership.userId,
// newRole: req.body.role,
// oldRole: membership.role,
// email: ""
// }
// }
// });
return { roles };
} }
}); });
@@ -1,10 +1,12 @@
import { z } from "zod"; import { z } from "zod";
import { import {
AccessScope,
IdentitiesSchema, IdentitiesSchema,
IdentityProjectMembershipsSchema, IdentityProjectMembershipsSchema,
ProjectMembershipRole, ProjectMembershipRole,
ProjectUserMembershipRolesSchema ProjectUserMembershipRolesSchema,
TemporaryPermissionMode
} from "@app/db/schemas"; } from "@app/db/schemas";
import { ApiDocsTags, ORGANIZATIONS, PROJECT_IDENTITIES } from "@app/lib/api-docs"; import { ApiDocsTags, ORGANIZATIONS, PROJECT_IDENTITIES } from "@app/lib/api-docs";
import { BadRequestError } from "@app/lib/errors"; import { BadRequestError } from "@app/lib/errors";
@@ -14,7 +16,6 @@ import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type"; import { AuthMode } from "@app/services/auth/auth-type";
import { ProjectIdentityOrderBy } from "@app/services/identity-project/identity-project-types"; import { ProjectIdentityOrderBy } from "@app/services/identity-project/identity-project-types";
import { ProjectUserMembershipTemporaryMode } from "@app/services/project-membership/project-membership-types";
import { SanitizedProjectSchema } from "../sanitizedSchemas"; import { SanitizedProjectSchema } from "../sanitizedSchemas";
@@ -56,7 +57,7 @@ export const registerIdentityProjectRouter = async (server: FastifyZodProvider)
role: z.string().describe(PROJECT_IDENTITIES.CREATE_IDENTITY_MEMBERSHIP.roles.role), role: z.string().describe(PROJECT_IDENTITIES.CREATE_IDENTITY_MEMBERSHIP.roles.role),
isTemporary: z.literal(true).describe(PROJECT_IDENTITIES.CREATE_IDENTITY_MEMBERSHIP.roles.role), isTemporary: z.literal(true).describe(PROJECT_IDENTITIES.CREATE_IDENTITY_MEMBERSHIP.roles.role),
temporaryMode: z temporaryMode: z
.nativeEnum(ProjectUserMembershipTemporaryMode) .nativeEnum(TemporaryPermissionMode)
.describe(PROJECT_IDENTITIES.CREATE_IDENTITY_MEMBERSHIP.roles.role), .describe(PROJECT_IDENTITIES.CREATE_IDENTITY_MEMBERSHIP.roles.role),
temporaryRange: z temporaryRange: z
.string() .string()
@@ -82,16 +83,22 @@ export const registerIdentityProjectRouter = async (server: FastifyZodProvider)
const { role, roles } = req.body; const { role, roles } = req.body;
if (!role && !roles) throw new BadRequestError({ message: "You must provide either role or roles field" }); if (!role && !roles) throw new BadRequestError({ message: "You must provide either role or roles field" });
const identityMembership = await server.services.identityProject.createProjectIdentity({ const { membership } = await server.services.membershipIdentity.createMembership({
actor: req.permission.type, permission: req.permission,
actorId: req.permission.id, scopeData: {
actorAuthMethod: req.permission.authMethod, scope: AccessScope.Project,
actorOrgId: req.permission.orgId, orgId: req.permission.orgId,
identityId: req.params.identityId, projectId: req.params.projectId
projectId: req.params.projectId, },
roles: roles || [{ role }] data: {
identityId: req.params.identityId,
roles: roles || [{ role, isTemporary: false }]
}
}); });
return { identityMembership };
return {
identityMembership: { ...membership, identityId: req.params.identityId, projectId: req.params.projectId }
};
} }
}); });
@@ -130,7 +137,7 @@ export const registerIdentityProjectRouter = async (server: FastifyZodProvider)
role: z.string().describe(PROJECT_IDENTITIES.UPDATE_IDENTITY_MEMBERSHIP.roles.role), role: z.string().describe(PROJECT_IDENTITIES.UPDATE_IDENTITY_MEMBERSHIP.roles.role),
isTemporary: z.literal(true).describe(PROJECT_IDENTITIES.UPDATE_IDENTITY_MEMBERSHIP.roles.isTemporary), isTemporary: z.literal(true).describe(PROJECT_IDENTITIES.UPDATE_IDENTITY_MEMBERSHIP.roles.isTemporary),
temporaryMode: z temporaryMode: z
.nativeEnum(ProjectUserMembershipTemporaryMode) .nativeEnum(TemporaryPermissionMode)
.describe(PROJECT_IDENTITIES.UPDATE_IDENTITY_MEMBERSHIP.roles.temporaryMode), .describe(PROJECT_IDENTITIES.UPDATE_IDENTITY_MEMBERSHIP.roles.temporaryMode),
temporaryRange: z temporaryRange: z
.string() .string()
@@ -153,16 +160,24 @@ export const registerIdentityProjectRouter = async (server: FastifyZodProvider)
} }
}, },
handler: async (req) => { handler: async (req) => {
const roles = await server.services.identityProject.updateProjectIdentity({ const { membership } = await server.services.membershipIdentity.updateMembership({
actor: req.permission.type, permission: req.permission,
actorId: req.permission.id, scopeData: {
actorAuthMethod: req.permission.authMethod, scope: AccessScope.Project,
actorOrgId: req.permission.orgId, orgId: req.permission.orgId,
identityId: req.params.identityId, projectId: req.params.projectId
projectId: req.params.projectId, },
roles: req.body.roles selector: {
identityId: req.params.identityId
},
data: {
roles: req.body.roles
}
}); });
return { roles };
return {
roles: membership.roles.map((el) => ({ ...el, projectMembershipId: membership.id }))
};
} }
}); });
@@ -193,15 +208,21 @@ export const registerIdentityProjectRouter = async (server: FastifyZodProvider)
} }
}, },
handler: async (req) => { handler: async (req) => {
const identityMembership = await server.services.identityProject.deleteProjectIdentity({ const { membership } = await server.services.membershipIdentity.deleteMembership({
actor: req.permission.type, permission: req.permission,
actorId: req.permission.id, scopeData: {
actorAuthMethod: req.permission.authMethod, scope: AccessScope.Project,
actorOrgId: req.permission.orgId, orgId: req.permission.orgId,
identityId: req.params.identityId, projectId: req.params.projectId
projectId: req.params.projectId },
selector: {
identityId: req.params.identityId
}
}); });
return { identityMembership };
return {
identityMembership: { ...membership, identityId: req.params.identityId, projectId: req.params.projectId }
};
} }
}); });
@@ -362,6 +383,7 @@ export const registerIdentityProjectRouter = async (server: FastifyZodProvider)
} }
}); });
// TODO(simp): look into how to handle projects
server.route({ server.route({
method: "GET", method: "GET",
url: "/identity-memberships/:identityMembershipId", url: "/identity-memberships/:identityMembershipId",
@@ -1,53 +1,45 @@
import { Knex } from "knex"; import { Knex } from "knex";
import { TDbClient } from "@app/db"; import { TDbClient } from "@app/db";
import { TableName, TUserEncryptionKeys } from "@app/db/schemas"; import { AccessScope, TableName, TMemberships, TUserEncryptionKeys } from "@app/db/schemas";
import { DatabaseError } from "@app/lib/errors"; import { DatabaseError } from "@app/lib/errors";
import { ormify, sqlNestRelationships } from "@app/lib/knex"; import { sqlNestRelationships } from "@app/lib/knex";
export type TGroupProjectDALFactory = ReturnType<typeof groupProjectDALFactory>; export type TGroupProjectDALFactory = ReturnType<typeof groupProjectDALFactory>;
export const groupProjectDALFactory = (db: TDbClient) => { export const groupProjectDALFactory = (db: TDbClient) => {
const groupProjectOrm = ormify(db, TableName.GroupProjectMembership);
const findByProjectId = async (projectId: string, filter?: { groupId?: string }, tx?: Knex) => { const findByProjectId = async (projectId: string, filter?: { groupId?: string }, tx?: Knex) => {
try { try {
const docs = await (tx || db.replicaNode())(TableName.GroupProjectMembership) const docs = await (tx || db.replicaNode())(TableName.Membership)
.where(`${TableName.GroupProjectMembership}.projectId`, projectId) .where(`${TableName.Membership}.scopeProjectId`, projectId)
.where(`${TableName.Membership}.scope`, AccessScope.Project)
.whereNotNull(`${TableName.Membership}.actorGroupId`)
.where((qb) => { .where((qb) => {
if (filter?.groupId) { if (filter?.groupId) {
void qb.where(`${TableName.Groups}.id`, "=", filter.groupId); void qb.where(`${TableName.Groups}.id`, "=", filter.groupId);
} }
}) })
.join(TableName.Groups, `${TableName.GroupProjectMembership}.groupId`, `${TableName.Groups}.id`) .join(TableName.Groups, `${TableName.Membership}.actorGroupId`, `${TableName.Groups}.id`)
.join( .join(TableName.MembershipRole, `${TableName.MembershipRole}.membershipId`, `${TableName.Membership}.id`)
TableName.GroupProjectMembershipRole, .leftJoin(TableName.Role, `${TableName.MembershipRole}.customRoleId`, `${TableName.Role}.id`)
`${TableName.GroupProjectMembershipRole}.projectMembershipId`,
`${TableName.GroupProjectMembership}.id`
)
.leftJoin(
TableName.ProjectRoles,
`${TableName.GroupProjectMembershipRole}.customRoleId`,
`${TableName.ProjectRoles}.id`
)
.select( .select(
db.ref("id").withSchema(TableName.GroupProjectMembership), db.ref("id").withSchema(TableName.Membership),
db.ref("createdAt").withSchema(TableName.GroupProjectMembership), db.ref("createdAt").withSchema(TableName.Membership),
db.ref("updatedAt").withSchema(TableName.GroupProjectMembership), db.ref("updatedAt").withSchema(TableName.Membership),
db.ref("id").as("groupId").withSchema(TableName.Groups), db.ref("id").as("groupId").withSchema(TableName.Groups),
db.ref("name").as("groupName").withSchema(TableName.Groups), db.ref("name").as("groupName").withSchema(TableName.Groups),
db.ref("slug").as("groupSlug").withSchema(TableName.Groups), db.ref("slug").as("groupSlug").withSchema(TableName.Groups),
db.ref("id").withSchema(TableName.GroupProjectMembership), db.ref("id").withSchema(TableName.Membership),
db.ref("role").withSchema(TableName.GroupProjectMembershipRole), db.ref("role").withSchema(TableName.MembershipRole),
db.ref("id").withSchema(TableName.GroupProjectMembershipRole).as("membershipRoleId"), db.ref("id").withSchema(TableName.MembershipRole).as("membershipRoleId"),
db.ref("customRoleId").withSchema(TableName.GroupProjectMembershipRole), db.ref("customRoleId").withSchema(TableName.MembershipRole),
db.ref("name").withSchema(TableName.ProjectRoles).as("customRoleName"), db.ref("name").withSchema(TableName.Role).as("customRoleName"),
db.ref("slug").withSchema(TableName.ProjectRoles).as("customRoleSlug"), db.ref("slug").withSchema(TableName.Role).as("customRoleSlug"),
db.ref("temporaryMode").withSchema(TableName.GroupProjectMembershipRole), db.ref("temporaryMode").withSchema(TableName.MembershipRole),
db.ref("isTemporary").withSchema(TableName.GroupProjectMembershipRole), db.ref("isTemporary").withSchema(TableName.MembershipRole),
db.ref("temporaryRange").withSchema(TableName.GroupProjectMembershipRole), db.ref("temporaryRange").withSchema(TableName.MembershipRole),
db.ref("temporaryAccessStartTime").withSchema(TableName.GroupProjectMembershipRole), db.ref("temporaryAccessStartTime").withSchema(TableName.MembershipRole),
db.ref("temporaryAccessEndTime").withSchema(TableName.GroupProjectMembershipRole) db.ref("temporaryAccessEndTime").withSchema(TableName.MembershipRole)
); );
const members = sqlNestRelationships({ const members = sqlNestRelationships({
@@ -104,8 +96,8 @@ export const groupProjectDALFactory = (db: TDbClient) => {
try { try {
const docs = await (tx || db.replicaNode())(TableName.UserGroupMembership) const docs = await (tx || db.replicaNode())(TableName.UserGroupMembership)
.where(`${TableName.UserGroupMembership}.userId`, userId) .where(`${TableName.UserGroupMembership}.userId`, userId)
.join(TableName.Groups, function () { .join(TableName.Groups, (qb) => {
this.on(`${TableName.UserGroupMembership}.groupId`, "=", `${TableName.Groups}.id`).andOn( qb.on(`${TableName.UserGroupMembership}.groupId`, "=", `${TableName.Groups}.id`).andOn(
`${TableName.Groups}.orgId`, `${TableName.Groups}.orgId`,
"=", "=",
db.raw("?", [orgId]) db.raw("?", [orgId])
@@ -131,32 +123,26 @@ export const groupProjectDALFactory = (db: TDbClient) => {
const docs = await db(TableName.UserGroupMembership) const docs = await db(TableName.UserGroupMembership)
// Join the GroupProjectMembership table with the Groups table to get the group name and slug. // Join the GroupProjectMembership table with the Groups table to get the group name and slug.
.join( .join(
TableName.GroupProjectMembership, TableName.Membership,
`${TableName.UserGroupMembership}.groupId`, `${TableName.UserGroupMembership}.groupId`,
`${TableName.GroupProjectMembership}.groupId` // this gives us access to the project id in the group membership `${TableName.Membership}.actorGroupId` // this gives us access to the project id in the group membership
) )
.join(TableName.Project, `${TableName.Membership}.scopeProjectId`, `${TableName.Project}.id`)
.join(TableName.Project, `${TableName.GroupProjectMembership}.projectId`, `${TableName.Project}.id`) .where(`${TableName.Membership}.scopeProjectId`, projectId)
.where(`${TableName.Membership}.scope`, AccessScope.Project)
.where(`${TableName.GroupProjectMembership}.projectId`, projectId)
.join(TableName.Users, `${TableName.UserGroupMembership}.userId`, `${TableName.Users}.id`) .join(TableName.Users, `${TableName.UserGroupMembership}.userId`, `${TableName.Users}.id`)
.join<TUserEncryptionKeys>( .join<TUserEncryptionKeys>(
TableName.UserEncryptionKey, TableName.UserEncryptionKey,
`${TableName.UserEncryptionKey}.userId`, `${TableName.UserEncryptionKey}.userId`,
`${TableName.Users}.id` `${TableName.Users}.id`
) )
.join( .join(TableName.MembershipRole, `${TableName.MembershipRole}.membershipId`, `${TableName.Membership}.id`)
TableName.GroupProjectMembershipRole, .leftJoin(TableName.Role, `${TableName.MembershipRole}.customRoleId`, `${TableName.Role}.id`)
`${TableName.GroupProjectMembershipRole}.projectMembershipId`, .join<TMemberships>(db(TableName.Membership).as("orgMembership"), (qb) => {
`${TableName.GroupProjectMembership}.id` qb.on(`${TableName.Users}.id`, `orgMembership.userId`)
) .andOn(`orgMembership.scope`, db.raw("?", [AccessScope.Organization]))
.leftJoin( .andOn(`orgMembership.scopeOrgId`, `${TableName.Project}.orgId`);
TableName.ProjectRoles, })
`${TableName.GroupProjectMembershipRole}.customRoleId`,
`${TableName.ProjectRoles}.id`
)
.join(TableName.OrgMembership, `${TableName.Users}.id`, `${TableName.OrgMembership}.userId`)
.select( .select(
db.ref("id").withSchema(TableName.UserGroupMembership), db.ref("id").withSchema(TableName.UserGroupMembership),
db.ref("createdAt").withSchema(TableName.UserGroupMembership), db.ref("createdAt").withSchema(TableName.UserGroupMembership),
@@ -167,18 +153,18 @@ export const groupProjectDALFactory = (db: TDbClient) => {
db.ref("firstName").withSchema(TableName.Users), db.ref("firstName").withSchema(TableName.Users),
db.ref("lastName").withSchema(TableName.Users), db.ref("lastName").withSchema(TableName.Users),
db.ref("id").withSchema(TableName.Users).as("userId"), db.ref("id").withSchema(TableName.Users).as("userId"),
db.ref("role").withSchema(TableName.GroupProjectMembershipRole), db.ref("role").withSchema(TableName.MembershipRole),
db.ref("id").withSchema(TableName.GroupProjectMembershipRole).as("membershipRoleId"), db.ref("id").withSchema(TableName.MembershipRole).as("membershipRoleId"),
db.ref("customRoleId").withSchema(TableName.GroupProjectMembershipRole), db.ref("customRoleId").withSchema(TableName.MembershipRole),
db.ref("name").withSchema(TableName.ProjectRoles).as("customRoleName"), db.ref("name").withSchema(TableName.Role).as("customRoleName"),
db.ref("slug").withSchema(TableName.ProjectRoles).as("customRoleSlug"), db.ref("slug").withSchema(TableName.Role).as("customRoleSlug"),
db.ref("temporaryMode").withSchema(TableName.GroupProjectMembershipRole), db.ref("temporaryMode").withSchema(TableName.MembershipRole),
db.ref("isTemporary").withSchema(TableName.GroupProjectMembershipRole), db.ref("isTemporary").withSchema(TableName.MembershipRole),
db.ref("temporaryRange").withSchema(TableName.GroupProjectMembershipRole), db.ref("temporaryRange").withSchema(TableName.MembershipRole),
db.ref("temporaryAccessStartTime").withSchema(TableName.GroupProjectMembershipRole), db.ref("temporaryAccessStartTime").withSchema(TableName.MembershipRole),
db.ref("temporaryAccessEndTime").withSchema(TableName.GroupProjectMembershipRole), db.ref("temporaryAccessEndTime").withSchema(TableName.MembershipRole),
db.ref("name").as("projectName").withSchema(TableName.Project), db.ref("name").as("projectName").withSchema(TableName.Project),
db.ref("isActive").withSchema(TableName.OrgMembership) db.ref("isActive").withSchema("orgMembership")
) )
.where({ isGhost: false }); .where({ isGhost: false });
@@ -242,5 +228,5 @@ export const groupProjectDALFactory = (db: TDbClient) => {
return members; return members;
}; };
return { ...groupProjectOrm, findByProjectId, findByUserId, findAllProjectGroupMembers }; return { findByProjectId, findByUserId, findAllProjectGroupMembers };
}; };
@@ -1,10 +0,0 @@
import { TDbClient } from "@app/db";
import { TableName } from "@app/db/schemas";
import { ormify } from "@app/lib/knex";
export type TGroupProjectMembershipRoleDALFactory = ReturnType<typeof groupProjectMembershipRoleDALFactory>;
export const groupProjectMembershipRoleDALFactory = (db: TDbClient) => {
const orm = ormify(db, TableName.GroupProjectMembershipRole);
return orm;
};
@@ -2,6 +2,7 @@ import { Knex } from "knex";
import { TDbClient } from "@app/db"; import { TDbClient } from "@app/db";
import { import {
AccessScope,
TableName, TableName,
TIdentities, TIdentities,
TIdentityAlicloudAuths, TIdentityAlicloudAuths,
@@ -15,7 +16,7 @@ import {
TIdentityUniversalAuths TIdentityUniversalAuths
} from "@app/db/schemas"; } from "@app/db/schemas";
import { DatabaseError } from "@app/lib/errors"; import { DatabaseError } from "@app/lib/errors";
import { ormify, selectAllTableCols, sqlNestRelationships } from "@app/lib/knex"; import { selectAllTableCols, sqlNestRelationships } from "@app/lib/knex";
import { OrderByDirection } from "@app/lib/types"; import { OrderByDirection } from "@app/lib/types";
import { ProjectIdentityOrderBy, TListProjectIdentityDTO } from "@app/services/identity-project/identity-project-types"; import { ProjectIdentityOrderBy, TListProjectIdentityDTO } from "@app/services/identity-project/identity-project-types";
@@ -24,96 +25,82 @@ import { buildAuthMethods } from "../identity/identity-fns";
export type TIdentityProjectDALFactory = ReturnType<typeof identityProjectDALFactory>; export type TIdentityProjectDALFactory = ReturnType<typeof identityProjectDALFactory>;
export const identityProjectDALFactory = (db: TDbClient) => { export const identityProjectDALFactory = (db: TDbClient) => {
const identityProjectOrm = ormify(db, TableName.IdentityProjectMembership);
const findByIdentityId = async (identityId: string, tx?: Knex) => { const findByIdentityId = async (identityId: string, tx?: Knex) => {
try { try {
const docs = await (tx || db.replicaNode())(TableName.IdentityProjectMembership) const docs = await (tx || db.replicaNode())(TableName.Membership)
.where(`${TableName.IdentityProjectMembership}.identityId`, identityId) .where(`${TableName.Membership}.actorIdentityId`, identityId)
.join(TableName.Project, `${TableName.IdentityProjectMembership}.projectId`, `${TableName.Project}.id`) .where(`${TableName.Membership}.scope`, AccessScope.Project)
.join(TableName.Identity, `${TableName.IdentityProjectMembership}.identityId`, `${TableName.Identity}.id`) .whereNotNull(`${TableName.Membership}.actorIdentityId`)
.join( .join(TableName.Project, `${TableName.Membership}.scopeProjectId`, `${TableName.Project}.id`)
TableName.IdentityProjectMembershipRole, .join(TableName.Identity, `${TableName.Membership}.actorIdentityId`, `${TableName.Identity}.id`)
`${TableName.IdentityProjectMembershipRole}.projectMembershipId`, .join(TableName.MembershipRole, `${TableName.MembershipRole}.membershipId`, `${TableName.Membership}.id`)
`${TableName.IdentityProjectMembership}.id` .leftJoin(TableName.Role, `${TableName.MembershipRole}.customRoleId`, `${TableName.Role}.id`)
)
.leftJoin(
TableName.ProjectRoles,
`${TableName.IdentityProjectMembershipRole}.customRoleId`,
`${TableName.ProjectRoles}.id`
)
.leftJoin(
TableName.IdentityProjectAdditionalPrivilege,
`${TableName.IdentityProjectMembership}.id`,
`${TableName.IdentityProjectAdditionalPrivilege}.projectMembershipId`
)
.leftJoin( .leftJoin(
TableName.IdentityUniversalAuth, TableName.IdentityUniversalAuth,
`${TableName.IdentityProjectMembership}.identityId`, `${TableName.Membership}.actorIdentityId`,
`${TableName.IdentityUniversalAuth}.identityId` `${TableName.IdentityUniversalAuth}.identityId`
) )
.leftJoin( .leftJoin(
TableName.IdentityGcpAuth, TableName.IdentityGcpAuth,
`${TableName.IdentityProjectMembership}.identityId`, `${TableName.Membership}.actorIdentityId`,
`${TableName.IdentityGcpAuth}.identityId` `${TableName.IdentityGcpAuth}.identityId`
) )
.leftJoin( .leftJoin(
TableName.IdentityAliCloudAuth, TableName.IdentityAliCloudAuth,
`${TableName.IdentityProjectMembership}.identityId`, `${TableName.Membership}.actorIdentityId`,
`${TableName.IdentityAliCloudAuth}.identityId` `${TableName.IdentityAliCloudAuth}.identityId`
) )
.leftJoin( .leftJoin(
TableName.IdentityAwsAuth, TableName.IdentityAwsAuth,
`${TableName.IdentityProjectMembership}.identityId`, `${TableName.Membership}.actorIdentityId`,
`${TableName.IdentityAwsAuth}.identityId` `${TableName.IdentityAwsAuth}.identityId`
) )
.leftJoin( .leftJoin(
TableName.IdentityKubernetesAuth, TableName.IdentityKubernetesAuth,
`${TableName.IdentityProjectMembership}.identityId`, `${TableName.Membership}.actorIdentityId`,
`${TableName.IdentityKubernetesAuth}.identityId` `${TableName.IdentityKubernetesAuth}.identityId`
) )
.leftJoin( .leftJoin(
TableName.IdentityOciAuth, TableName.IdentityOciAuth,
`${TableName.IdentityProjectMembership}.identityId`, `${TableName.Membership}.actorIdentityId`,
`${TableName.IdentityOciAuth}.identityId` `${TableName.IdentityOciAuth}.identityId`
) )
.leftJoin( .leftJoin(
TableName.IdentityOidcAuth, TableName.IdentityOidcAuth,
`${TableName.IdentityProjectMembership}.identityId`, `${TableName.Membership}.actorIdentityId`,
`${TableName.IdentityOidcAuth}.identityId` `${TableName.IdentityOidcAuth}.identityId`
) )
.leftJoin( .leftJoin(
TableName.IdentityAzureAuth, TableName.IdentityAzureAuth,
`${TableName.IdentityProjectMembership}.identityId`, `${TableName.Membership}.actorIdentityId`,
`${TableName.IdentityAzureAuth}.identityId` `${TableName.IdentityAzureAuth}.identityId`
) )
.leftJoin( .leftJoin(
TableName.IdentityTokenAuth, TableName.IdentityTokenAuth,
`${TableName.IdentityProjectMembership}.identityId`, `${TableName.Membership}.actorIdentityId`,
`${TableName.IdentityTokenAuth}.identityId` `${TableName.IdentityTokenAuth}.identityId`
) )
.select( .select(
db.ref("id").withSchema(TableName.IdentityProjectMembership), db.ref("id").withSchema(TableName.Membership),
db.ref("createdAt").withSchema(TableName.IdentityProjectMembership), db.ref("createdAt").withSchema(TableName.Membership),
db.ref("updatedAt").withSchema(TableName.IdentityProjectMembership), db.ref("updatedAt").withSchema(TableName.Membership),
db.ref("id").as("identityId").withSchema(TableName.Identity), db.ref("id").as("identityId").withSchema(TableName.Identity),
db.ref("name").as("identityName").withSchema(TableName.Identity), db.ref("name").as("identityName").withSchema(TableName.Identity),
db.ref("hasDeleteProtection").withSchema(TableName.Identity), db.ref("hasDeleteProtection").withSchema(TableName.Identity),
db.ref("id").withSchema(TableName.IdentityProjectMembership), db.ref("id").withSchema(TableName.Membership),
db.ref("role").withSchema(TableName.IdentityProjectMembershipRole), db.ref("role").withSchema(TableName.MembershipRole),
db.ref("id").withSchema(TableName.IdentityProjectMembershipRole).as("membershipRoleId"), db.ref("id").withSchema(TableName.MembershipRole).as("membershipRoleId"),
db.ref("customRoleId").withSchema(TableName.IdentityProjectMembershipRole), db.ref("customRoleId").withSchema(TableName.MembershipRole),
db.ref("name").withSchema(TableName.ProjectRoles).as("customRoleName"), db.ref("name").withSchema(TableName.Role).as("customRoleName"),
db.ref("slug").withSchema(TableName.ProjectRoles).as("customRoleSlug"), db.ref("slug").withSchema(TableName.Role).as("customRoleSlug"),
db.ref("temporaryMode").withSchema(TableName.IdentityProjectMembershipRole), db.ref("temporaryMode").withSchema(TableName.MembershipRole),
db.ref("isTemporary").withSchema(TableName.IdentityProjectMembershipRole), db.ref("isTemporary").withSchema(TableName.MembershipRole),
db.ref("temporaryRange").withSchema(TableName.IdentityProjectMembershipRole), db.ref("temporaryRange").withSchema(TableName.MembershipRole),
db.ref("temporaryAccessStartTime").withSchema(TableName.IdentityProjectMembershipRole), db.ref("temporaryAccessStartTime").withSchema(TableName.MembershipRole),
db.ref("temporaryAccessEndTime").withSchema(TableName.IdentityProjectMembershipRole), db.ref("temporaryAccessEndTime").withSchema(TableName.MembershipRole),
db.ref("projectId").withSchema(TableName.IdentityProjectMembership), db.ref("scopeProjectId").withSchema(TableName.Membership).as("projectId"),
db.ref("name").as("projectName").withSchema(TableName.Project), db.ref("name").as("projectName").withSchema(TableName.Project),
db.ref("type").as("projectType").withSchema(TableName.Project), db.ref("type").as("projectType").withSchema(TableName.Project),
db.ref("id").as("uaId").withSchema(TableName.IdentityUniversalAuth), db.ref("id").as("uaId").withSchema(TableName.IdentityUniversalAuth),
@@ -165,7 +152,7 @@ export const identityProjectDALFactory = (db: TDbClient) => {
}) })
}, },
project: { project: {
id: projectId, id: projectId as string,
name: projectName, name: projectName,
type: projectType type: projectType
} }
@@ -223,12 +210,10 @@ export const identityProjectDALFactory = (db: TDbClient) => {
void qb.whereILike(`${TableName.Identity}.name`, `%${filter.search}%`); void qb.whereILike(`${TableName.Identity}.name`, `%${filter.search}%`);
} }
}) })
.join( .join(TableName.Membership, `${TableName.Membership}.actorIdentityId`, `${TableName.Identity}.id`)
TableName.IdentityProjectMembership, .where(`${TableName.Membership}.scopeProjectId`, projectId)
`${TableName.IdentityProjectMembership}.identityId`, .where(`${TableName.Membership}.scope`, AccessScope.Project)
`${TableName.Identity}.id` .whereNotNull(`${TableName.Membership}.actorIdentityId`)
)
.where(`${TableName.IdentityProjectMembership}.projectId`, projectId)
.orderBy( .orderBy(
`${TableName.Identity}.${filter.orderBy ?? ProjectIdentityOrderBy.Name}`, `${TableName.Identity}.${filter.orderBy ?? ProjectIdentityOrderBy.Name}`,
filter.orderDirection ?? OrderByDirection.ASC filter.orderDirection ?? OrderByDirection.ASC
@@ -240,33 +225,20 @@ export const identityProjectDALFactory = (db: TDbClient) => {
void fetchIdentitySubquery.offset(filter.offset ?? 0).limit(filter.limit); void fetchIdentitySubquery.offset(filter.offset ?? 0).limit(filter.limit);
} }
const query = (tx || db.replicaNode())(TableName.IdentityProjectMembership) const query = (tx || db.replicaNode())(TableName.Membership)
.where(`${TableName.IdentityProjectMembership}.projectId`, projectId) .where(`${TableName.Membership}.scopeProjectId`, projectId)
.join(TableName.Project, `${TableName.IdentityProjectMembership}.projectId`, `${TableName.Project}.id`) .where(`${TableName.Membership}.scope`, AccessScope.Project)
.join(TableName.Project, `${TableName.Membership}.scopeProjectId`, `${TableName.Project}.id`)
.join<TIdentities, TIdentities>(fetchIdentitySubquery, (bd) => { .join<TIdentities, TIdentities>(fetchIdentitySubquery, (bd) => {
bd.on(`${TableName.IdentityProjectMembership}.identityId`, `${TableName.Identity}.id`); bd.on(`${TableName.Membership}.actorIdentityId`, `${TableName.Identity}.id`);
}) })
.where((qb) => { .where((qb) => {
if (filter.identityId) { if (filter.identityId) {
void qb.where(`${TableName.IdentityProjectMembership}.identityId`, filter.identityId); void qb.where(`${TableName.Membership}.actorIdentityId`, filter.identityId);
} }
}) })
.join( .join(TableName.MembershipRole, `${TableName.MembershipRole}.membershipId`, `${TableName.Membership}.id`)
TableName.IdentityProjectMembershipRole, .leftJoin(TableName.Role, `${TableName.MembershipRole}.customRoleId`, `${TableName.Role}.id`)
`${TableName.IdentityProjectMembershipRole}.projectMembershipId`,
`${TableName.IdentityProjectMembership}.id`
)
.leftJoin(
TableName.ProjectRoles,
`${TableName.IdentityProjectMembershipRole}.customRoleId`,
`${TableName.ProjectRoles}.id`
)
.leftJoin(
TableName.IdentityProjectAdditionalPrivilege,
`${TableName.IdentityProjectMembership}.id`,
`${TableName.IdentityProjectAdditionalPrivilege}.projectMembershipId`
)
.leftJoin<TIdentityUniversalAuths>( .leftJoin<TIdentityUniversalAuths>(
TableName.IdentityUniversalAuth, TableName.IdentityUniversalAuth,
`${TableName.Identity}.id`, `${TableName.Identity}.id`,
@@ -314,23 +286,23 @@ export const identityProjectDALFactory = (db: TDbClient) => {
) )
.select( .select(
db.ref("id").withSchema(TableName.IdentityProjectMembership), db.ref("id").withSchema(TableName.Membership),
db.ref("createdAt").withSchema(TableName.IdentityProjectMembership), db.ref("createdAt").withSchema(TableName.Membership),
db.ref("updatedAt").withSchema(TableName.IdentityProjectMembership), db.ref("updatedAt").withSchema(TableName.Membership),
db.ref("authMethod").as("identityAuthMethod").withSchema(TableName.Identity), db.ref("authMethod").as("identityAuthMethod").withSchema(TableName.Identity),
db.ref("id").as("identityId").withSchema(TableName.Identity), db.ref("id").as("identityId").withSchema(TableName.Identity),
db.ref("name").as("identityName").withSchema(TableName.Identity), db.ref("name").as("identityName").withSchema(TableName.Identity),
db.ref("id").withSchema(TableName.IdentityProjectMembership), db.ref("id").withSchema(TableName.Membership),
db.ref("role").withSchema(TableName.IdentityProjectMembershipRole), db.ref("role").withSchema(TableName.MembershipRole),
db.ref("id").withSchema(TableName.IdentityProjectMembershipRole).as("membershipRoleId"), db.ref("id").withSchema(TableName.MembershipRole).as("membershipRoleId"),
db.ref("customRoleId").withSchema(TableName.IdentityProjectMembershipRole), db.ref("customRoleId").withSchema(TableName.MembershipRole),
db.ref("name").withSchema(TableName.ProjectRoles).as("customRoleName"), db.ref("name").withSchema(TableName.Role).as("customRoleName"),
db.ref("slug").withSchema(TableName.ProjectRoles).as("customRoleSlug"), db.ref("slug").withSchema(TableName.Role).as("customRoleSlug"),
db.ref("temporaryMode").withSchema(TableName.IdentityProjectMembershipRole), db.ref("temporaryMode").withSchema(TableName.MembershipRole),
db.ref("isTemporary").withSchema(TableName.IdentityProjectMembershipRole), db.ref("isTemporary").withSchema(TableName.MembershipRole),
db.ref("temporaryRange").withSchema(TableName.IdentityProjectMembershipRole), db.ref("temporaryRange").withSchema(TableName.MembershipRole),
db.ref("temporaryAccessStartTime").withSchema(TableName.IdentityProjectMembershipRole), db.ref("temporaryAccessStartTime").withSchema(TableName.MembershipRole),
db.ref("temporaryAccessEndTime").withSchema(TableName.IdentityProjectMembershipRole), db.ref("temporaryAccessEndTime").withSchema(TableName.MembershipRole),
db.ref("name").as("projectName").withSchema(TableName.Project), db.ref("name").as("projectName").withSchema(TableName.Project),
db.ref("id").as("uaId").withSchema(TableName.IdentityUniversalAuth), db.ref("id").as("uaId").withSchema(TableName.IdentityUniversalAuth),
db.ref("id").as("gcpId").withSchema(TableName.IdentityGcpAuth), db.ref("id").as("gcpId").withSchema(TableName.IdentityGcpAuth),
@@ -450,10 +422,11 @@ export const identityProjectDALFactory = (db: TDbClient) => {
tx?: Knex tx?: Knex
) => { ) => {
try { try {
const identities = await (tx || db.replicaNode())(TableName.IdentityProjectMembership) const identities = await (tx || db.replicaNode())(TableName.Membership)
.where(`${TableName.IdentityProjectMembership}.projectId`, projectId) .where(`${TableName.Membership}.scopeProjectId`, projectId)
.join(TableName.Project, `${TableName.IdentityProjectMembership}.projectId`, `${TableName.Project}.id`) .where(`${TableName.Membership}.scope`, AccessScope.Project)
.join(TableName.Identity, `${TableName.IdentityProjectMembership}.identityId`, `${TableName.Identity}.id`) .join(TableName.Project, `${TableName.Membership}.scopeProjectId`, `${TableName.Project}.id`)
.join(TableName.Identity, `${TableName.Membership}.actorIdentityId`, `${TableName.Identity}.id`)
.where((qb) => { .where((qb) => {
if (filter.identityId) { if (filter.identityId) {
void qb.where("identityId", filter.identityId); void qb.where("identityId", filter.identityId);
@@ -472,7 +445,6 @@ export const identityProjectDALFactory = (db: TDbClient) => {
}; };
return { return {
...identityProjectOrm,
findByIdentityId, findByIdentityId,
findByProjectId, findByProjectId,
getCountByProjectId getCountByProjectId
@@ -1,10 +0,0 @@
import { TDbClient } from "@app/db";
import { TableName } from "@app/db/schemas";
import { ormify } from "@app/lib/knex";
export type TIdentityProjectMembershipRoleDALFactory = ReturnType<typeof identityProjectMembershipRoleDALFactory>;
export const identityProjectMembershipRoleDALFactory = (db: TDbClient) => {
const orm = ormify(db, TableName.IdentityProjectMembershipRole);
return orm;
};
+102 -100
View File
@@ -2,6 +2,7 @@ import { Knex } from "knex";
import { TDbClient } from "@app/db"; import { TDbClient } from "@app/db";
import { import {
AccessScope,
TableName, TableName,
TIdentityAlicloudAuths, TIdentityAlicloudAuths,
TIdentityAwsAuths, TIdentityAwsAuths,
@@ -11,15 +12,15 @@ import {
TIdentityKubernetesAuths, TIdentityKubernetesAuths,
TIdentityOciAuths, TIdentityOciAuths,
TIdentityOidcAuths, TIdentityOidcAuths,
TIdentityOrgMemberships,
TIdentityTlsCertAuths, TIdentityTlsCertAuths,
TIdentityTokenAuths, TIdentityTokenAuths,
TIdentityUniversalAuths, TIdentityUniversalAuths,
TOrgRoles TMembershipRoles,
TMemberships
} from "@app/db/schemas"; } from "@app/db/schemas";
import { TIdentityLdapAuths } from "@app/db/schemas/identity-ldap-auths"; import { TIdentityLdapAuths } from "@app/db/schemas/identity-ldap-auths";
import { BadRequestError, DatabaseError } from "@app/lib/errors"; import { BadRequestError, DatabaseError } from "@app/lib/errors";
import { ormify, selectAllTableCols, sqlNestRelationships } from "@app/lib/knex"; import { selectAllTableCols, sqlNestRelationships } from "@app/lib/knex";
import { buildKnexFilterForSearchResource } from "@app/lib/search-resource/db"; import { buildKnexFilterForSearchResource } from "@app/lib/search-resource/db";
import { OrderByDirection } from "@app/lib/types"; import { OrderByDirection } from "@app/lib/types";
import { import {
@@ -33,80 +34,80 @@ import { buildAuthMethods } from "./identity-fns";
export type TIdentityOrgDALFactory = ReturnType<typeof identityOrgDALFactory>; export type TIdentityOrgDALFactory = ReturnType<typeof identityOrgDALFactory>;
export const identityOrgDALFactory = (db: TDbClient) => { export const identityOrgDALFactory = (db: TDbClient) => {
const identityOrgOrm = ormify(db, TableName.IdentityOrgMembership); const findOne = async (filter: Partial<TMemberships>, tx?: Knex) => {
const findOne = async (filter: Partial<TIdentityOrgMemberships>, tx?: Knex) => {
try { try {
const [data] = await (tx || db.replicaNode())(TableName.IdentityOrgMembership) const [data] = await (tx || db.replicaNode())(TableName.Membership)
.where(`${TableName.Membership}.scope`, AccessScope.Organization)
.whereNotNull(`${TableName.Membership}.actorIdentityId`)
.where((queryBuilder) => { .where((queryBuilder) => {
Object.entries(filter).forEach(([key, value]) => { Object.entries(filter).forEach(([key, value]) => {
void queryBuilder.where(`${TableName.IdentityOrgMembership}.${key}`, value); void queryBuilder.where(`${TableName.Membership}.${key}`, value);
}); });
}) })
.join(TableName.Identity, `${TableName.IdentityOrgMembership}.identityId`, `${TableName.Identity}.id`) .join(TableName.Identity, `${TableName.Membership}.actorIdentityId`, `${TableName.Identity}.id`)
.leftJoin<TIdentityUniversalAuths>( .leftJoin<TIdentityUniversalAuths>(
TableName.IdentityUniversalAuth, TableName.IdentityUniversalAuth,
`${TableName.IdentityOrgMembership}.identityId`, `${TableName.Membership}.actorIdentityId`,
`${TableName.IdentityUniversalAuth}.identityId` `${TableName.IdentityUniversalAuth}.identityId`
) )
.leftJoin<TIdentityGcpAuths>( .leftJoin<TIdentityGcpAuths>(
TableName.IdentityGcpAuth, TableName.IdentityGcpAuth,
`${TableName.IdentityOrgMembership}.identityId`, `${TableName.Membership}.actorIdentityId`,
`${TableName.IdentityGcpAuth}.identityId` `${TableName.IdentityGcpAuth}.identityId`
) )
.leftJoin<TIdentityAlicloudAuths>( .leftJoin<TIdentityAlicloudAuths>(
TableName.IdentityAliCloudAuth, TableName.IdentityAliCloudAuth,
`${TableName.IdentityOrgMembership}.identityId`, `${TableName.Membership}.actorIdentityId`,
`${TableName.IdentityAliCloudAuth}.identityId` `${TableName.IdentityAliCloudAuth}.identityId`
) )
.leftJoin<TIdentityAwsAuths>( .leftJoin<TIdentityAwsAuths>(
TableName.IdentityAwsAuth, TableName.IdentityAwsAuth,
`${TableName.IdentityOrgMembership}.identityId`, `${TableName.Membership}.actorIdentityId`,
`${TableName.IdentityAwsAuth}.identityId` `${TableName.IdentityAwsAuth}.identityId`
) )
.leftJoin<TIdentityKubernetesAuths>( .leftJoin<TIdentityKubernetesAuths>(
TableName.IdentityKubernetesAuth, TableName.IdentityKubernetesAuth,
`${TableName.IdentityOrgMembership}.identityId`, `${TableName.Membership}.actorIdentityId`,
`${TableName.IdentityKubernetesAuth}.identityId` `${TableName.IdentityKubernetesAuth}.identityId`
) )
.leftJoin<TIdentityOciAuths>( .leftJoin<TIdentityOciAuths>(
TableName.IdentityOciAuth, TableName.IdentityOciAuth,
`${TableName.IdentityOrgMembership}.identityId`, `${TableName.Membership}.actorIdentityId`,
`${TableName.IdentityOciAuth}.identityId` `${TableName.IdentityOciAuth}.identityId`
) )
.leftJoin<TIdentityOidcAuths>( .leftJoin<TIdentityOidcAuths>(
TableName.IdentityOidcAuth, TableName.IdentityOidcAuth,
`${TableName.IdentityOrgMembership}.identityId`, `${TableName.Membership}.actorIdentityId`,
`${TableName.IdentityOidcAuth}.identityId` `${TableName.IdentityOidcAuth}.identityId`
) )
.leftJoin<TIdentityAzureAuths>( .leftJoin<TIdentityAzureAuths>(
TableName.IdentityAzureAuth, TableName.IdentityAzureAuth,
`${TableName.IdentityOrgMembership}.identityId`, `${TableName.Membership}.actorIdentityId`,
`${TableName.IdentityAzureAuth}.identityId` `${TableName.IdentityAzureAuth}.identityId`
) )
.leftJoin<TIdentityTokenAuths>( .leftJoin<TIdentityTokenAuths>(
TableName.IdentityTokenAuth, TableName.IdentityTokenAuth,
`${TableName.IdentityOrgMembership}.identityId`, `${TableName.Membership}.actorIdentityId`,
`${TableName.IdentityTokenAuth}.identityId` `${TableName.IdentityTokenAuth}.identityId`
) )
.leftJoin<TIdentityJwtAuths>( .leftJoin<TIdentityJwtAuths>(
TableName.IdentityJwtAuth, TableName.IdentityJwtAuth,
`${TableName.IdentityOrgMembership}.identityId`, `${TableName.Membership}.actorIdentityId`,
`${TableName.IdentityJwtAuth}.identityId` `${TableName.IdentityJwtAuth}.identityId`
) )
.leftJoin<TIdentityLdapAuths>( .leftJoin<TIdentityLdapAuths>(
TableName.IdentityLdapAuth, TableName.IdentityLdapAuth,
`${TableName.IdentityOrgMembership}.identityId`, `${TableName.Membership}.actorIdentityId`,
`${TableName.IdentityLdapAuth}.identityId` `${TableName.IdentityLdapAuth}.identityId`
) )
.leftJoin<TIdentityTlsCertAuths>( .leftJoin<TIdentityTlsCertAuths>(
TableName.IdentityTlsCertAuth, TableName.IdentityTlsCertAuth,
`${TableName.IdentityOrgMembership}.identityId`, `${TableName.Membership}.actorIdentityId`,
`${TableName.IdentityTlsCertAuth}.identityId` `${TableName.IdentityTlsCertAuth}.identityId`
) )
.select( .select(
selectAllTableCols(TableName.IdentityOrgMembership), selectAllTableCols(TableName.Membership),
db.ref("id").as("uaId").withSchema(TableName.IdentityUniversalAuth), db.ref("id").as("uaId").withSchema(TableName.IdentityUniversalAuth),
db.ref("id").as("gcpId").withSchema(TableName.IdentityGcpAuth), db.ref("id").as("gcpId").withSchema(TableName.IdentityGcpAuth),
@@ -129,7 +130,7 @@ export const identityOrgDALFactory = (db: TDbClient) => {
return { return {
...data, ...data,
identity: { identity: {
id: data.identityId, id: data.actorIdentityId as string,
name, name,
hasDeleteProtection, hasDeleteProtection,
authMethods: buildAuthMethods(data) authMethods: buildAuthMethods(data)
@@ -149,20 +150,18 @@ export const identityOrgDALFactory = (db: TDbClient) => {
orderDirection = OrderByDirection.ASC, orderDirection = OrderByDirection.ASC,
search, search,
...filter ...filter
}: Partial<TIdentityOrgMemberships> & }: Partial<TMemberships> &
Pick<TListOrgIdentitiesByOrgIdDTO, "offset" | "limit" | "orderBy" | "orderDirection" | "search">, Pick<TListOrgIdentitiesByOrgIdDTO, "offset" | "limit" | "orderBy" | "orderDirection" | "search">,
tx?: Knex tx?: Knex
) => { ) => {
try { try {
const paginatedIdentity = (tx || db.replicaNode())(TableName.Identity) const paginatedIdentity = (tx || db.replicaNode())(TableName.Identity)
.join( .join(TableName.Membership, `${TableName.Membership}.actorIdentityId`, `${TableName.Identity}.id`)
TableName.IdentityOrgMembership, .where(`${TableName.Membership}.scope`, AccessScope.Organization)
`${TableName.IdentityOrgMembership}.identityId`, .whereNotNull(`${TableName.Membership}.actorIdentityId`)
`${TableName.Identity}.id`
)
.orderBy(`${TableName.Identity}.${orderBy}`, orderDirection) .orderBy(`${TableName.Identity}.${orderBy}`, orderDirection)
.select( .select(
selectAllTableCols(TableName.IdentityOrgMembership), selectAllTableCols(TableName.Membership),
db.ref("name").withSchema(TableName.Identity).as("identityName"), db.ref("name").withSchema(TableName.Identity).as("identityName"),
db.ref("hasDeleteProtection").withSchema(TableName.Identity) db.ref("hasDeleteProtection").withSchema(TableName.Identity)
) )
@@ -181,14 +180,17 @@ export const identityOrgDALFactory = (db: TDbClient) => {
type TSubquery = Awaited<typeof paginatedIdentity>; type TSubquery = Awaited<typeof paginatedIdentity>;
const query = (tx || db.replicaNode()) const query = (tx || db.replicaNode())
.from<TSubquery[number], TSubquery>(paginatedIdentity) .from<TSubquery[number], TSubquery>(paginatedIdentity)
.leftJoin<TOrgRoles>(TableName.OrgRoles, `paginatedIdentity.roleId`, `${TableName.OrgRoles}.id`) .join<TMembershipRoles>(
TableName.MembershipRole,
`${TableName.MembershipRole}.membershipId`,
"paginatedIdentity.id"
)
.leftJoin(TableName.Role, `${TableName.MembershipRole}.customRoleId`, `${TableName.Role}.id`)
.leftJoin(TableName.IdentityMetadata, (queryBuilder) => { .leftJoin(TableName.IdentityMetadata, (queryBuilder) => {
void queryBuilder void queryBuilder
.on(`paginatedIdentity.identityId`, `${TableName.IdentityMetadata}.identityId`) .on(`paginatedIdentity.actorIdentityId`, `${TableName.IdentityMetadata}.identityId`)
.andOn(`paginatedIdentity.orgId`, `${TableName.IdentityMetadata}.orgId`); .andOn(`paginatedIdentity.scopeOrgId`, `${TableName.IdentityMetadata}.orgId`);
}) })
.leftJoin<TIdentityUniversalAuths>( .leftJoin<TIdentityUniversalAuths>(
TableName.IdentityUniversalAuth, TableName.IdentityUniversalAuth,
"paginatedIdentity.identityId", "paginatedIdentity.identityId",
@@ -251,14 +253,14 @@ export const identityOrgDALFactory = (db: TDbClient) => {
) )
.select( .select(
db.ref("id").withSchema("paginatedIdentity"), db.ref("id").withSchema("paginatedIdentity"),
db.ref("role").withSchema("paginatedIdentity"), db.ref("role").withSchema(TableName.MembershipRole),
db.ref("roleId").withSchema("paginatedIdentity"), db.ref("customRoleId").withSchema(TableName.MembershipRole).as("roleId"),
db.ref("orgId").withSchema("paginatedIdentity"), db.ref("scopeOrgId").withSchema("paginatedIdentity").as("orgId"),
db.ref("lastLoginAuthMethod").withSchema("paginatedIdentity"), db.ref("lastLoginAuthMethod").withSchema("paginatedIdentity"),
db.ref("lastLoginTime").withSchema("paginatedIdentity"), db.ref("lastLoginTime").withSchema("paginatedIdentity"),
db.ref("createdAt").withSchema("paginatedIdentity"), db.ref("createdAt").withSchema("paginatedIdentity"),
db.ref("updatedAt").withSchema("paginatedIdentity"), db.ref("updatedAt").withSchema("paginatedIdentity"),
db.ref("identityId").withSchema("paginatedIdentity").as("identityId"), db.ref("actorIdentityId").withSchema("paginatedIdentity").as("identityId"),
db.ref("identityName").withSchema("paginatedIdentity"), db.ref("identityName").withSchema("paginatedIdentity"),
db.ref("hasDeleteProtection").withSchema("paginatedIdentity"), db.ref("hasDeleteProtection").withSchema("paginatedIdentity"),
@@ -276,12 +278,11 @@ export const identityOrgDALFactory = (db: TDbClient) => {
db.ref("id").as("tlsCertId").withSchema(TableName.IdentityTlsCertAuth) db.ref("id").as("tlsCertId").withSchema(TableName.IdentityTlsCertAuth)
) )
// cr stands for custom role // cr stands for custom role
.select(db.ref("id").as("crId").withSchema(TableName.OrgRoles)) .select(db.ref("id").as("crId").withSchema(TableName.Role))
.select(db.ref("name").as("crName").withSchema(TableName.OrgRoles)) .select(db.ref("name").as("crName").withSchema(TableName.Role))
.select(db.ref("slug").as("crSlug").withSchema(TableName.OrgRoles)) .select(db.ref("slug").as("crSlug").withSchema(TableName.Role))
.select(db.ref("description").as("crDescription").withSchema(TableName.OrgRoles)) .select(db.ref("description").as("crDescription").withSchema(TableName.Role))
.select(db.ref("permissions").as("crPermission").withSchema(TableName.OrgRoles)) .select(db.ref("permissions").as("crPermission").withSchema(TableName.Role))
.select(db.ref("permissions").as("crPermission").withSchema(TableName.OrgRoles))
.select( .select(
db.ref("id").withSchema(TableName.IdentityMetadata).as("metadataId"), db.ref("id").withSchema(TableName.IdentityMetadata).as("metadataId"),
db.ref("key").withSchema(TableName.IdentityMetadata).as("metadataKey"), db.ref("key").withSchema(TableName.IdentityMetadata).as("metadataKey"),
@@ -327,7 +328,7 @@ export const identityOrgDALFactory = (db: TDbClient) => {
}) => ({ }) => ({
role, role,
roleId, roleId,
identityId, identityId: identityId as string,
id, id,
orgId, orgId,
createdAt, createdAt,
@@ -344,7 +345,7 @@ export const identityOrgDALFactory = (db: TDbClient) => {
} }
: undefined, : undefined,
identity: { identity: {
id: identityId, id: identityId as string,
name: identityName, name: identityName,
hasDeleteProtection, hasDeleteProtection,
authMethods: buildAuthMethods({ authMethods: buildAuthMethods({
@@ -394,20 +395,23 @@ export const identityOrgDALFactory = (db: TDbClient) => {
tx?: Knex tx?: Knex
) => { ) => {
try { try {
const searchQuery = (tx || db.replicaNode())(TableName.IdentityOrgMembership) const searchQuery = (tx || db.replicaNode())(TableName.Membership)
.join(TableName.Identity, `${TableName.Identity}.id`, `${TableName.IdentityOrgMembership}.identityId`) .where(`${TableName.Membership}.scope`, AccessScope.Organization)
.where(`${TableName.IdentityOrgMembership}.orgId`, orgId) .whereNotNull(`${TableName.Membership}.actorIdentityId`)
.leftJoin(TableName.OrgRoles, `${TableName.IdentityOrgMembership}.roleId`, `${TableName.OrgRoles}.id`) .where(`${TableName.Membership}.scopeOrgId`, orgId)
.join(TableName.Identity, `${TableName.Identity}.id`, `${TableName.Membership}.actorIdentityId`)
.join(TableName.MembershipRole, `${TableName.MembershipRole}.membershipId`, `${TableName.Membership}.id`)
.leftJoin(TableName.Role, `${TableName.MembershipRole}.customRoleId`, `${TableName.Role}.id`)
.orderBy( .orderBy(
orderBy === OrgIdentityOrderBy.Role orderBy === OrgIdentityOrderBy.Role
? `${TableName.IdentityOrgMembership}.${orderBy}` ? `${TableName.Membership}.${orderBy}`
: `${TableName.Identity}.${orderBy}`, : `${TableName.Identity}.${orderBy}`,
orderDirection orderDirection
) )
.select(`${TableName.IdentityOrgMembership}.id`) .select(`${TableName.Membership}.id`)
.select<{ id: string; total_count: string }>( .select<{ id: string; total_count: string }>(
db.raw( db.raw(
`count(${TableName.IdentityOrgMembership}."identityId") OVER(PARTITION BY ${TableName.IdentityOrgMembership}."orgId") as total_count` `count(${TableName.Membership}."actorIdentityId") OVER(PARTITION BY ${TableName.Membership}."scopeOrgId") as total_count`
) )
) )
.as("searchedIdentities"); .as("searchedIdentities");
@@ -416,7 +420,7 @@ export const identityOrgDALFactory = (db: TDbClient) => {
buildKnexFilterForSearchResource(searchQuery, searchFilter, (attr) => { buildKnexFilterForSearchResource(searchQuery, searchFilter, (attr) => {
switch (attr) { switch (attr) {
case "role": case "role":
return [`${TableName.OrgRoles}.slug`, `${TableName.IdentityOrgMembership}.role`]; return [`${TableName.Role}.slug`, `${TableName.MembershipRole}.role`];
case "name": case "name":
return `${TableName.Identity}.name`; return `${TableName.Identity}.name`;
default: default:
@@ -430,82 +434,85 @@ export const identityOrgDALFactory = (db: TDbClient) => {
} }
type TSubquery = Awaited<typeof searchQuery>; type TSubquery = Awaited<typeof searchQuery>;
const query = (tx || db.replicaNode())(TableName.IdentityOrgMembership) const query = (tx || db.replicaNode())(TableName.Membership)
.where(`${TableName.IdentityOrgMembership}.orgId`, orgId) .where(`${TableName.Membership}.scope`, AccessScope.Organization)
.join<TSubquery>(searchQuery, `${TableName.IdentityOrgMembership}.id`, "searchedIdentities.id") .whereNotNull(`${TableName.Membership}.actorIdentityId`)
.join(TableName.Identity, `${TableName.IdentityOrgMembership}.identityId`, `${TableName.Identity}.id`) .where(`${TableName.Membership}.scopeOrgId`, orgId)
.leftJoin(TableName.OrgRoles, `${TableName.IdentityOrgMembership}.roleId`, `${TableName.OrgRoles}.id`) .join<TSubquery>(searchQuery, `${TableName.Membership}.id`, "searchedIdentities.id")
.join(TableName.Identity, `${TableName.Membership}.actorIdentityId`, `${TableName.Identity}.id`)
.join(TableName.MembershipRole, `${TableName.MembershipRole}.membershipId`, `${TableName.Membership}.id`)
.leftJoin(TableName.Role, `${TableName.MembershipRole}.customRoleId`, `${TableName.Role}.id`)
.leftJoin(TableName.IdentityMetadata, (queryBuilder) => { .leftJoin(TableName.IdentityMetadata, (queryBuilder) => {
void queryBuilder void queryBuilder
.on(`${TableName.IdentityOrgMembership}.identityId`, `${TableName.IdentityMetadata}.identityId`) .on(`${TableName.Membership}.actorIdentityId`, `${TableName.IdentityMetadata}.identityId`)
.andOn(`${TableName.IdentityOrgMembership}.orgId`, `${TableName.IdentityMetadata}.orgId`); .andOn(`${TableName.Membership}.scopeOrgId`, `${TableName.IdentityMetadata}.orgId`);
}) })
.leftJoin( .leftJoin(
TableName.IdentityUniversalAuth, TableName.IdentityUniversalAuth,
`${TableName.IdentityOrgMembership}.identityId`, `${TableName.Membership}.actorIdentityId`,
`${TableName.IdentityUniversalAuth}.identityId` `${TableName.IdentityUniversalAuth}.identityId`
) )
.leftJoin( .leftJoin(
TableName.IdentityGcpAuth, TableName.IdentityGcpAuth,
`${TableName.IdentityOrgMembership}.identityId`, `${TableName.Membership}.actorIdentityId`,
`${TableName.IdentityGcpAuth}.identityId` `${TableName.IdentityGcpAuth}.identityId`
) )
.leftJoin( .leftJoin(
TableName.IdentityAliCloudAuth, TableName.IdentityAliCloudAuth,
`${TableName.IdentityOrgMembership}.identityId`, `${TableName.Membership}.actorIdentityId`,
`${TableName.IdentityAliCloudAuth}.identityId` `${TableName.IdentityAliCloudAuth}.identityId`
) )
.leftJoin( .leftJoin(
TableName.IdentityAwsAuth, TableName.IdentityAwsAuth,
`${TableName.IdentityOrgMembership}.identityId`, `${TableName.Membership}.actorIdentityId`,
`${TableName.IdentityAwsAuth}.identityId` `${TableName.IdentityAwsAuth}.identityId`
) )
.leftJoin( .leftJoin(
TableName.IdentityKubernetesAuth, TableName.IdentityKubernetesAuth,
`${TableName.IdentityOrgMembership}.identityId`, `${TableName.Membership}.actorIdentityId`,
`${TableName.IdentityKubernetesAuth}.identityId` `${TableName.IdentityKubernetesAuth}.identityId`
) )
.leftJoin( .leftJoin(
TableName.IdentityOciAuth, TableName.IdentityOciAuth,
`${TableName.IdentityOrgMembership}.identityId`, `${TableName.Membership}.actorIdentityId`,
`${TableName.IdentityOciAuth}.identityId` `${TableName.IdentityOciAuth}.identityId`
) )
.leftJoin( .leftJoin(
TableName.IdentityOidcAuth, TableName.IdentityOidcAuth,
`${TableName.IdentityOrgMembership}.identityId`, `${TableName.Membership}.actorIdentityId`,
`${TableName.IdentityOidcAuth}.identityId` `${TableName.IdentityOidcAuth}.identityId`
) )
.leftJoin( .leftJoin(
TableName.IdentityAzureAuth, TableName.IdentityAzureAuth,
`${TableName.IdentityOrgMembership}.identityId`, `${TableName.Membership}.actorIdentityId`,
`${TableName.IdentityAzureAuth}.identityId` `${TableName.IdentityAzureAuth}.identityId`
) )
.leftJoin( .leftJoin(
TableName.IdentityTokenAuth, TableName.IdentityTokenAuth,
`${TableName.IdentityOrgMembership}.identityId`, `${TableName.Membership}.actorIdentityId`,
`${TableName.IdentityTokenAuth}.identityId` `${TableName.IdentityTokenAuth}.identityId`
) )
.leftJoin( .leftJoin(
TableName.IdentityJwtAuth, TableName.IdentityJwtAuth,
`${TableName.IdentityOrgMembership}.identityId`, `${TableName.Membership}.actorIdentityId`,
`${TableName.IdentityJwtAuth}.identityId` `${TableName.IdentityJwtAuth}.identityId`
) )
.leftJoin( .leftJoin(
TableName.IdentityLdapAuth, TableName.IdentityLdapAuth,
`${TableName.IdentityOrgMembership}.identityId`, `${TableName.Membership}.actorIdentityId`,
`${TableName.IdentityLdapAuth}.identityId` `${TableName.IdentityLdapAuth}.identityId`
) )
.select( .select(
db.ref("id").withSchema(TableName.IdentityOrgMembership), db.ref("id").withSchema(TableName.Membership),
db.ref("total_count").withSchema("searchedIdentities"), db.ref("total_count").withSchema("searchedIdentities"),
db.ref("role").withSchema(TableName.IdentityOrgMembership), db.ref("role").withSchema(TableName.MembershipRole),
db.ref("roleId").withSchema(TableName.IdentityOrgMembership), db.ref("customRoleId").withSchema(TableName.MembershipRole).as("roleId"),
db.ref("orgId").withSchema(TableName.IdentityOrgMembership), db.ref("scopeOrgId").withSchema(TableName.Membership).as("orgId"),
db.ref("createdAt").withSchema(TableName.IdentityOrgMembership), db.ref("createdAt").withSchema(TableName.Membership),
db.ref("updatedAt").withSchema(TableName.IdentityOrgMembership), db.ref("updatedAt").withSchema(TableName.Membership),
db.ref("lastLoginAuthMethod").withSchema(TableName.IdentityOrgMembership), db.ref("lastLoginAuthMethod").withSchema(TableName.Membership),
db.ref("lastLoginTime").withSchema(TableName.IdentityOrgMembership), db.ref("lastLoginTime").withSchema(TableName.Membership),
db.ref("identityId").withSchema(TableName.IdentityOrgMembership).as("identityId"), db.ref("actorIdentityId").withSchema(TableName.Membership).as("identityId"),
db.ref("name").withSchema(TableName.Identity).as("identityName"), db.ref("name").withSchema(TableName.Identity).as("identityName"),
db.ref("hasDeleteProtection").withSchema(TableName.Identity), db.ref("hasDeleteProtection").withSchema(TableName.Identity),
@@ -522,12 +529,11 @@ export const identityOrgDALFactory = (db: TDbClient) => {
db.ref("id").as("ldapId").withSchema(TableName.IdentityLdapAuth) db.ref("id").as("ldapId").withSchema(TableName.IdentityLdapAuth)
) )
// cr stands for custom role // cr stands for custom role
.select(db.ref("id").as("crId").withSchema(TableName.OrgRoles)) .select(db.ref("id").as("crId").withSchema(TableName.Role))
.select(db.ref("name").as("crName").withSchema(TableName.OrgRoles)) .select(db.ref("name").as("crName").withSchema(TableName.Role))
.select(db.ref("slug").as("crSlug").withSchema(TableName.OrgRoles)) .select(db.ref("slug").as("crSlug").withSchema(TableName.Role))
.select(db.ref("description").as("crDescription").withSchema(TableName.OrgRoles)) .select(db.ref("description").as("crDescription").withSchema(TableName.Role))
.select(db.ref("permissions").as("crPermission").withSchema(TableName.OrgRoles)) .select(db.ref("permissions").as("crPermission").withSchema(TableName.Role))
.select(db.ref("permissions").as("crPermission").withSchema(TableName.OrgRoles))
.select( .select(
db.ref("id").withSchema(TableName.IdentityMetadata).as("metadataId"), db.ref("id").withSchema(TableName.IdentityMetadata).as("metadataId"),
db.ref("key").withSchema(TableName.IdentityMetadata).as("metadataKey"), db.ref("key").withSchema(TableName.IdentityMetadata).as("metadataKey"),
@@ -545,13 +551,7 @@ export const identityOrgDALFactory = (db: TDbClient) => {
ELSE ??.role ELSE ??.role
END ? END ?
`, `,
[ [TableName.MembershipRole, "custom", TableName.Role, TableName.MembershipRole, db.raw(orderDirection)]
TableName.IdentityOrgMembership,
"custom",
TableName.OrgRoles,
TableName.IdentityOrgMembership,
db.raw(orderDirection)
]
); );
} }
@@ -590,7 +590,7 @@ export const identityOrgDALFactory = (db: TDbClient) => {
}) => ({ }) => ({
role, role,
roleId, roleId,
identityId, identityId: identityId as string,
id, id,
total_count: total_count as string, total_count: total_count as string,
orgId, orgId,
@@ -646,13 +646,15 @@ export const identityOrgDALFactory = (db: TDbClient) => {
}; };
const countAllOrgIdentities = async ( const countAllOrgIdentities = async (
{ search, ...filter }: Partial<TIdentityOrgMemberships> & Pick<TListOrgIdentitiesByOrgIdDTO, "search">, { search, ...filter }: Partial<TMemberships> & Pick<TListOrgIdentitiesByOrgIdDTO, "search">,
tx?: Knex tx?: Knex
) => { ) => {
try { try {
const query = (tx || db.replicaNode())(TableName.IdentityOrgMembership) const query = (tx || db.replicaNode())(TableName.Membership)
.where(`${TableName.Membership}.scope`, AccessScope.Organization)
.whereNotNull(`${TableName.Membership}.actorIdentityId`)
.where(filter) .where(filter)
.join(TableName.Identity, `${TableName.IdentityOrgMembership}.identityId`, `${TableName.Identity}.id`) .join(TableName.Identity, `${TableName.Membership}.actorIdentityId`, `${TableName.Identity}.id`)
.count(); .count();
if (search?.length) { if (search?.length) {
@@ -667,5 +669,5 @@ export const identityOrgDALFactory = (db: TDbClient) => {
} }
}; };
return { ...identityOrgOrm, find, findOne, countAllOrgIdentities, searchIdentities }; return { find, findOne, countAllOrgIdentities, searchIdentities };
}; };
@@ -239,8 +239,8 @@ export const membershipIdentityServiceFactory = ({
}, },
tx tx
); );
await membershipRoleDAL.insertMany(roleDocs, tx); const insertedRoleDocs = await membershipRoleDAL.insertMany(roleDocs, tx);
return doc; return { ...doc, roles: insertedRoleDocs };
}); });
return { membership: membershipDoc }; return { membership: membershipDoc };
@@ -1,19 +1,20 @@
import { TDbClient } from "@app/db"; import { TDbClient } from "@app/db";
import { TableName, TUserEncryptionKeys } from "@app/db/schemas"; import { AccessScope, TableName, TUserEncryptionKeys } from "@app/db/schemas";
import { DatabaseError } from "@app/lib/errors"; import { DatabaseError } from "@app/lib/errors";
import { ormify, sqlNestRelationships } from "@app/lib/knex"; import { sqlNestRelationships } from "@app/lib/knex";
export type TOrgMembershipDALFactory = ReturnType<typeof orgMembershipDALFactory>; export type TOrgMembershipDALFactory = ReturnType<typeof orgMembershipDALFactory>;
export const orgMembershipDALFactory = (db: TDbClient) => { export const orgMembershipDALFactory = (db: TDbClient) => {
const orgMembershipOrm = ormify(db, TableName.OrgMembership);
const findOrgMembershipById = async (membershipId: string) => { const findOrgMembershipById = async (membershipId: string) => {
try { try {
const member = await db const member = await db
.replicaNode()(TableName.OrgMembership) .replicaNode()(TableName.Membership)
.where(`${TableName.OrgMembership}.id`, membershipId) .where(`${TableName.Membership}.id`, membershipId)
.join(TableName.Users, `${TableName.OrgMembership}.userId`, `${TableName.Users}.id`) .where(`${TableName.Membership}.scope`, AccessScope.Organization)
.whereNotNull(`${TableName.Membership}.actorUserId`)
.join(TableName.Users, `${TableName.Membership}.actorUserId`, `${TableName.Users}.id`)
.join(TableName.MembershipRole, `${TableName.Membership}.id`, `${TableName.MembershipRole}.membershipId`)
.leftJoin<TUserEncryptionKeys>( .leftJoin<TUserEncryptionKeys>(
TableName.UserEncryptionKey, TableName.UserEncryptionKey,
`${TableName.UserEncryptionKey}.userId`, `${TableName.UserEncryptionKey}.userId`,
@@ -21,19 +22,19 @@ export const orgMembershipDALFactory = (db: TDbClient) => {
) )
.leftJoin(TableName.IdentityMetadata, (queryBuilder) => { .leftJoin(TableName.IdentityMetadata, (queryBuilder) => {
void queryBuilder void queryBuilder
.on(`${TableName.OrgMembership}.userId`, `${TableName.IdentityMetadata}.userId`) .on(`${TableName.Membership}.actorUserId`, `${TableName.IdentityMetadata}.userId`)
.andOn(`${TableName.OrgMembership}.orgId`, `${TableName.IdentityMetadata}.orgId`); .andOn(`${TableName.Membership}.scopeOrgId`, `${TableName.IdentityMetadata}.orgId`);
}) })
.select( .select(
db.ref("id").withSchema(TableName.OrgMembership), db.ref("id").withSchema(TableName.Membership),
db.ref("inviteEmail").withSchema(TableName.OrgMembership), db.ref("inviteEmail").withSchema(TableName.Membership),
db.ref("orgId").withSchema(TableName.OrgMembership), db.ref("scopeOrgId").withSchema(TableName.Membership).as("orgId"),
db.ref("role").withSchema(TableName.OrgMembership), db.ref("role").withSchema(TableName.MembershipRole),
db.ref("roleId").withSchema(TableName.OrgMembership), db.ref("customRoleId").withSchema(TableName.MembershipRole).as("roleId"),
db.ref("status").withSchema(TableName.OrgMembership), db.ref("status").withSchema(TableName.Membership),
db.ref("isActive").withSchema(TableName.OrgMembership), db.ref("isActive").withSchema(TableName.Membership),
db.ref("lastLoginAuthMethod").withSchema(TableName.OrgMembership), db.ref("lastLoginAuthMethod").withSchema(TableName.Membership),
db.ref("lastLoginTime").withSchema(TableName.OrgMembership), db.ref("lastLoginTime").withSchema(TableName.Membership),
db.ref("email").withSchema(TableName.Users), db.ref("email").withSchema(TableName.Users),
db.ref("username").withSchema(TableName.Users), db.ref("username").withSchema(TableName.Users),
db.ref("firstName").withSchema(TableName.Users), db.ref("firstName").withSchema(TableName.Users),
@@ -117,18 +118,20 @@ export const orgMembershipDALFactory = (db: TDbClient) => {
const twelveMonthsAgo = new Date(now.getTime() - 360 * 24 * 60 * 60 * 1000); const twelveMonthsAgo = new Date(now.getTime() - 360 * 24 * 60 * 60 * 1000);
const memberships = await db const memberships = await db
.replicaNode()(TableName.OrgMembership) .replicaNode()(TableName.Membership)
.where(`${TableName.Membership}.scope`, AccessScope.Organization)
.whereNotNull(`${TableName.Membership}.actorUserId`)
.where("status", "invited") .where("status", "invited")
.where((qb) => { .where((qb) => {
// lastInvitedAt is null AND createdAt is between 1 week and 12 months ago // lastInvitedAt is null AND createdAt is between 1 week and 12 months ago
void qb void qb
.whereNull(`${TableName.OrgMembership}.lastInvitedAt`) .whereNull(`${TableName.Membership}.lastInvitedAt`)
.whereBetween(`${TableName.OrgMembership}.createdAt`, [twelveMonthsAgo, oneWeekAgo]); .whereBetween(`${TableName.Membership}.createdAt`, [twelveMonthsAgo, oneWeekAgo]);
// lastInvitedAt is older than 1 week ago AND createdAt is younger than 1 month ago // lastInvitedAt is older than 1 week ago AND createdAt is younger than 1 month ago
void qb.orWhere((qbInner) => { void qb.orWhere((qbInner) => {
void qbInner void qbInner
.where(`${TableName.OrgMembership}.lastInvitedAt`, "<", oneWeekAgo) .where(`${TableName.Membership}.lastInvitedAt`, "<", oneWeekAgo)
.where(`${TableName.OrgMembership}.createdAt`, ">", oneMonthAgo); .where(`${TableName.Membership}.createdAt`, ">", oneMonthAgo);
}); });
}); });
@@ -144,7 +147,11 @@ export const orgMembershipDALFactory = (db: TDbClient) => {
const updateLastInvitedAtByIds = async (membershipIds: string[]) => { const updateLastInvitedAtByIds = async (membershipIds: string[]) => {
try { try {
if (membershipIds.length === 0) return; if (membershipIds.length === 0) return;
await db(TableName.OrgMembership).whereIn("id", membershipIds).update({ lastInvitedAt: new Date() }); await db(TableName.Membership)
.whereIn("id", membershipIds)
.where(`${TableName.Membership}.scope`, AccessScope.Organization)
.whereNotNull(`${TableName.Membership}.actorUserId`)
.update({ lastInvitedAt: new Date() });
} catch (error) { } catch (error) {
throw new DatabaseError({ throw new DatabaseError({
error, error,
@@ -156,9 +163,12 @@ export const orgMembershipDALFactory = (db: TDbClient) => {
const findOrgMembershipsWithUsersByOrgId = async (orgId: string) => { const findOrgMembershipsWithUsersByOrgId = async (orgId: string) => {
try { try {
const members = await db const members = await db
.replicaNode()(TableName.OrgMembership) .replicaNode()(TableName.Membership)
.where(`${TableName.OrgMembership}.orgId`, orgId) .where(`${TableName.Membership}.scopeOrgId`, orgId)
.join(TableName.Users, `${TableName.OrgMembership}.userId`, `${TableName.Users}.id`) .where(`${TableName.Membership}.scope`, AccessScope.Organization)
.whereNotNull(`${TableName.Membership}.actorUserId`)
.join(TableName.Users, `${TableName.Membership}.actorUserId`, `${TableName.Users}.id`)
.join(TableName.MembershipRole, `${TableName.Membership}.id`, `${TableName.MembershipRole}.membershipId`)
.leftJoin<TUserEncryptionKeys>( .leftJoin<TUserEncryptionKeys>(
TableName.UserEncryptionKey, TableName.UserEncryptionKey,
`${TableName.UserEncryptionKey}.userId`, `${TableName.UserEncryptionKey}.userId`,
@@ -166,17 +176,17 @@ export const orgMembershipDALFactory = (db: TDbClient) => {
) )
.leftJoin(TableName.IdentityMetadata, (queryBuilder) => { .leftJoin(TableName.IdentityMetadata, (queryBuilder) => {
void queryBuilder void queryBuilder
.on(`${TableName.OrgMembership}.userId`, `${TableName.IdentityMetadata}.userId`) .on(`${TableName.Membership}.actorUserId`, `${TableName.IdentityMetadata}.userId`)
.andOn(`${TableName.OrgMembership}.orgId`, `${TableName.IdentityMetadata}.orgId`); .andOn(`${TableName.Membership}.scopeOrgId`, `${TableName.IdentityMetadata}.orgId`);
}) })
.select( .select(
db.ref("id").withSchema(TableName.OrgMembership), db.ref("id").withSchema(TableName.Membership),
db.ref("inviteEmail").withSchema(TableName.OrgMembership), db.ref("inviteEmail").withSchema(TableName.Membership),
db.ref("orgId").withSchema(TableName.OrgMembership), db.ref("scopeOrgId").withSchema(TableName.Membership).as("orgId"),
db.ref("role").withSchema(TableName.OrgMembership), db.ref("role").withSchema(TableName.MembershipRole),
db.ref("roleId").withSchema(TableName.OrgMembership), db.ref("customRoleId").withSchema(TableName.MembershipRole).as("customRoleId"),
db.ref("status").withSchema(TableName.OrgMembership), db.ref("status").withSchema(TableName.Membership),
db.ref("isActive").withSchema(TableName.OrgMembership), db.ref("isActive").withSchema(TableName.Membership),
db.ref("email").withSchema(TableName.Users), db.ref("email").withSchema(TableName.Users),
db.ref("username").withSchema(TableName.Users), db.ref("username").withSchema(TableName.Users),
db.ref("firstName").withSchema(TableName.Users), db.ref("firstName").withSchema(TableName.Users),
@@ -207,7 +217,6 @@ export const orgMembershipDALFactory = (db: TDbClient) => {
}; };
return { return {
...orgMembershipOrm,
findOrgMembershipById, findOrgMembershipById,
findRecentInvitedMemberships, findRecentInvitedMemberships,
updateLastInvitedAtByIds, updateLastInvitedAtByIds,
+114 -127
View File
@@ -2,14 +2,15 @@ import { Knex } from "knex";
import { TDbClient } from "@app/db"; import { TDbClient } from "@app/db";
import { import {
AccessScope,
OrganizationsSchema, OrganizationsSchema,
OrgMembershipRole, OrgMembershipRole,
TableName, TableName,
TMemberships,
TMembershipsInsert,
TMembershipsUpdate,
TOrganizations, TOrganizations,
TOrganizationsInsert, TOrganizationsInsert,
TOrgMemberships,
TOrgMembershipsInsert,
TOrgMembershipsUpdate,
TUserEncryptionKeys TUserEncryptionKeys
} from "@app/db/schemas"; } from "@app/db/schemas";
import { DatabaseError } from "@app/lib/errors"; import { DatabaseError } from "@app/lib/errors";
@@ -64,9 +65,12 @@ export const orgDALFactory = (db: TDbClient) => {
.replicaNode()(TableName.Organization) .replicaNode()(TableName.Organization)
.whereIn(`${TableName.Organization}.id`, orgIdSubquery) .whereIn(`${TableName.Organization}.id`, orgIdSubquery)
.leftJoin(TableName.Project, `${TableName.Organization}.id`, `${TableName.Project}.orgId`) .leftJoin(TableName.Project, `${TableName.Organization}.id`, `${TableName.Project}.orgId`)
.leftJoin(TableName.OrgMembership, `${TableName.Organization}.id`, `${TableName.OrgMembership}.orgId`) .leftJoin(TableName.Membership, `${TableName.Organization}.id`, `${TableName.Membership}.scopeOrgId`)
.leftJoin(TableName.Users, `${TableName.OrgMembership}.userId`, `${TableName.Users}.id`) .where(`${TableName.Membership}.scope`, AccessScope.Organization)
.leftJoin(TableName.OrgRoles, `${TableName.OrgMembership}.roleId`, `${TableName.OrgRoles}.id`) .whereNotNull(`${TableName.Membership}.actorUserId`)
.leftJoin(TableName.Users, `${TableName.Membership}.actorUserId`, `${TableName.Users}.id`)
.leftJoin(TableName.MembershipRole, `${TableName.Membership}.id`, `${TableName.MembershipRole}.membershipId`)
.leftJoin(TableName.Role, `${TableName.MembershipRole}.customRoleId`, `${TableName.Role}.id`)
.where((qb) => { .where((qb) => {
void qb.where(`${TableName.Users}.isGhost`, false).orWhereNull(`${TableName.Users}.id`); void qb.where(`${TableName.Users}.isGhost`, false).orWhereNull(`${TableName.Users}.id`);
}); });
@@ -84,11 +88,11 @@ export const orgDALFactory = (db: TDbClient) => {
.select(db.ref("firstName").withSchema(TableName.Users).as("firstName")) .select(db.ref("firstName").withSchema(TableName.Users).as("firstName"))
.select(db.ref("lastName").withSchema(TableName.Users).as("lastName")) .select(db.ref("lastName").withSchema(TableName.Users).as("lastName"))
.select(db.ref("id").withSchema(TableName.Users).as("userId")) .select(db.ref("id").withSchema(TableName.Users).as("userId"))
.select(db.ref("id").withSchema(TableName.OrgMembership).as("orgMembershipId")) .select(db.ref("id").withSchema(TableName.Membership).as("orgMembershipId"))
.select(db.ref("role").withSchema(TableName.OrgMembership).as("orgMembershipRole")) .select(db.ref("status").withSchema(TableName.Membership).as("orgMembershipStatus"))
.select(db.ref("roleId").withSchema(TableName.OrgMembership).as("orgMembershipRoleId")) .select(db.ref("role").withSchema(TableName.MembershipRole).as("orgMembershipRole"))
.select(db.ref("status").withSchema(TableName.OrgMembership).as("orgMembershipStatus")) .select(db.ref("customRoleId").withSchema(TableName.MembershipRole).as("orgMembershipRoleId"))
.select(db.ref("name").withSchema(TableName.OrgRoles).as("orgMembershipRoleName")), .select(db.ref("name").withSchema(TableName.Role).as("orgMembershipRoleName")),
buildBaseQuery(countQuery).countDistinct(`${TableName.Organization}.id`, { as: "count" }).first() buildBaseQuery(countQuery).countDistinct(`${TableName.Organization}.id`, { as: "count" }).first()
]); ]);
@@ -229,9 +233,12 @@ export const orgDALFactory = (db: TDbClient) => {
): Promise<(TOrganizations & { orgAuthMethod: string; userRole: string; userStatus: string })[]> => { ): Promise<(TOrganizations & { orgAuthMethod: string; userRole: string; userStatus: string })[]> => {
try { try {
const org = (await db const org = (await db
.replicaNode()(TableName.OrgMembership) .replicaNode()(TableName.Membership)
.where({ userId }) .where(`${TableName.Membership}.actorUserId`, userId)
.join(TableName.Organization, `${TableName.OrgMembership}.orgId`, `${TableName.Organization}.id`) .where(`${TableName.Membership}.scope`, AccessScope.Organization)
.whereNotNull(`${TableName.Membership}.actorUserId`)
.join(TableName.MembershipRole, `${TableName.Membership}.id`, `${TableName.MembershipRole}.membershipId`)
.join(TableName.Organization, `${TableName.Membership}.scopeOrgId`, `${TableName.Organization}.id`)
.leftJoin(TableName.SamlConfig, (qb) => { .leftJoin(TableName.SamlConfig, (qb) => {
qb.on(`${TableName.SamlConfig}.orgId`, "=", `${TableName.Organization}.id`).andOn( qb.on(`${TableName.SamlConfig}.orgId`, "=", `${TableName.Organization}.id`).andOn(
`${TableName.SamlConfig}.isActive`, `${TableName.SamlConfig}.isActive`,
@@ -247,8 +254,8 @@ export const orgDALFactory = (db: TDbClient) => {
); );
}) })
.select(selectAllTableCols(TableName.Organization)) .select(selectAllTableCols(TableName.Organization))
.select(db.ref("role").withSchema(TableName.OrgMembership).as("userRole")) .select(db.ref("role").withSchema(TableName.MembershipRole).as("userRole"))
.select(db.ref("status").withSchema(TableName.OrgMembership).as("userStatus")) .select(db.ref("status").withSchema(TableName.Membership).as("userStatus"))
.select( .select(
db.raw(` db.raw(`
CASE CASE
@@ -283,24 +290,28 @@ export const orgDALFactory = (db: TDbClient) => {
const findAllOrgMembers = async (orgId: string) => { const findAllOrgMembers = async (orgId: string) => {
try { try {
const members = await db const members = await db
.replicaNode()(TableName.OrgMembership) .replicaNode()(TableName.Membership)
.where(`${TableName.OrgMembership}.orgId`, orgId) .where(`${TableName.Membership}.scopeOrgId`, orgId)
.join(TableName.Users, `${TableName.OrgMembership}.userId`, `${TableName.Users}.id`) .where(`${TableName.Membership}.scope`, AccessScope.Organization)
.whereNotNull(`${TableName.Membership}.actorUserId`)
.join(TableName.Users, `${TableName.Membership}.actorUserId`, `${TableName.Users}.id`)
.join(TableName.MembershipRole, `${TableName.Membership}.id`, `${TableName.MembershipRole}.membershipId`)
.leftJoin(TableName.Role, `${TableName.MembershipRole}.customRoleId`, `${TableName.Role}.id`)
.leftJoin<TUserEncryptionKeys>( .leftJoin<TUserEncryptionKeys>(
TableName.UserEncryptionKey, TableName.UserEncryptionKey,
`${TableName.UserEncryptionKey}.userId`, `${TableName.UserEncryptionKey}.userId`,
`${TableName.Users}.id` `${TableName.Users}.id`
) )
.select( .select(
db.ref("id").withSchema(TableName.OrgMembership), db.ref("id").withSchema(TableName.Membership),
db.ref("inviteEmail").withSchema(TableName.OrgMembership), db.ref("inviteEmail").withSchema(TableName.Membership),
db.ref("orgId").withSchema(TableName.OrgMembership), db.ref("actorOrgId").withSchema(TableName.Membership).as("orgId"),
db.ref("role").withSchema(TableName.OrgMembership), db.ref("role").withSchema(TableName.MembershipRole),
db.ref("roleId").withSchema(TableName.OrgMembership), db.ref("customRoleId").withSchema(TableName.MembershipRole).as("roleId"),
db.ref("status").withSchema(TableName.OrgMembership), db.ref("status").withSchema(TableName.Membership),
db.ref("isActive").withSchema(TableName.OrgMembership), db.ref("isActive").withSchema(TableName.Membership),
db.ref("lastLoginAuthMethod").withSchema(TableName.OrgMembership), db.ref("lastLoginAuthMethod").withSchema(TableName.Membership),
db.ref("lastLoginTime").withSchema(TableName.OrgMembership), db.ref("lastLoginTime").withSchema(TableName.Membership),
db.ref("email").withSchema(TableName.Users), db.ref("email").withSchema(TableName.Users),
db.ref("isEmailVerified").withSchema(TableName.Users), db.ref("isEmailVerified").withSchema(TableName.Users),
db.ref("username").withSchema(TableName.Users), db.ref("username").withSchema(TableName.Users),
@@ -332,11 +343,13 @@ export const orgDALFactory = (db: TDbClient) => {
} }
const count = await db const count = await db
.replicaNode()(TableName.OrgMembership) .replicaNode()(TableName.Membership)
.where(`${TableName.OrgMembership}.orgId`, orgId) .where(`${TableName.Membership}.orgId`, orgId)
.where(`${TableName.Membership}.scope`, AccessScope.Organization)
.whereNotNull(`${TableName.Membership}.actorUserId`)
.count("*") .count("*")
.join(TableName.Users, `${TableName.OrgMembership}.userId`, `${TableName.Users}.id`) .join(TableName.Users, `${TableName.Membership}.actorUserId`, `${TableName.Users}.id`)
.where({ isGhost: false, [`${TableName.OrgMembership}.isActive` as "isActive"]: true }) .where({ isGhost: false, [`${TableName.Membership}.isActive` as "isActive"]: true })
.first(); .first();
return parseInt((count as unknown as CountResult).count || "0", 10); return parseInt((count as unknown as CountResult).count || "0", 10);
@@ -348,21 +361,25 @@ export const orgDALFactory = (db: TDbClient) => {
const findOrgMembersByUsername = async (orgId: string, usernames: string[], tx?: Knex) => { const findOrgMembersByUsername = async (orgId: string, usernames: string[], tx?: Knex) => {
try { try {
const conn = tx || db.replicaNode(); const conn = tx || db.replicaNode();
const members = await conn(TableName.OrgMembership) const members = await conn(TableName.Membership)
.where(`${TableName.OrgMembership}.orgId`, orgId) .where(`${TableName.Membership}.scopeOrgId`, orgId)
.join(TableName.Users, `${TableName.OrgMembership}.userId`, `${TableName.Users}.id`) .where(`${TableName.Membership}.scope`, AccessScope.Organization)
.whereNotNull(`${TableName.Membership}.actorUserId`)
.join(TableName.Users, `${TableName.Membership}.actorUserId`, `${TableName.Users}.id`)
.join(TableName.MembershipRole, `${TableName.Membership}.id`, `${TableName.MembershipRole}.membershipId`)
.leftJoin(TableName.Role, `${TableName.MembershipRole}.customRoleId`, `${TableName.Role}.id`)
.leftJoin<TUserEncryptionKeys>( .leftJoin<TUserEncryptionKeys>(
TableName.UserEncryptionKey, TableName.UserEncryptionKey,
`${TableName.UserEncryptionKey}.userId`, `${TableName.UserEncryptionKey}.userId`,
`${TableName.Users}.id` `${TableName.Users}.id`
) )
.select( .select(
conn.ref("id").withSchema(TableName.OrgMembership), conn.ref("id").withSchema(TableName.Membership),
conn.ref("inviteEmail").withSchema(TableName.OrgMembership), conn.ref("inviteEmail").withSchema(TableName.Membership),
conn.ref("orgId").withSchema(TableName.OrgMembership), conn.ref("orgId").withSchema(TableName.Membership),
conn.ref("role").withSchema(TableName.OrgMembership), db.ref("role").withSchema(TableName.MembershipRole),
conn.ref("roleId").withSchema(TableName.OrgMembership), db.ref("customRoleId").withSchema(TableName.MembershipRole).as("roleId"),
conn.ref("status").withSchema(TableName.OrgMembership), conn.ref("status").withSchema(TableName.Membership),
conn.ref("username").withSchema(TableName.Users), conn.ref("username").withSchema(TableName.Users),
conn.ref("email").withSchema(TableName.Users), conn.ref("email").withSchema(TableName.Users),
conn.ref("firstName").withSchema(TableName.Users), conn.ref("firstName").withSchema(TableName.Users),
@@ -384,22 +401,25 @@ export const orgDALFactory = (db: TDbClient) => {
const findOrgMembersByRole = async (orgId: string, role: OrgMembershipRole, tx?: Knex) => { const findOrgMembersByRole = async (orgId: string, role: OrgMembershipRole, tx?: Knex) => {
try { try {
const conn = tx || db.replicaNode(); const conn = tx || db.replicaNode();
const members = await conn(TableName.OrgMembership) const members = await conn(TableName.Membership)
.where(`${TableName.OrgMembership}.orgId`, orgId) .where(`${TableName.Membership}.scopeOrgId`, orgId)
.where(`${TableName.OrgMembership}.role`, role) .where(`${TableName.Membership}.scope`, AccessScope.Organization)
.join(TableName.Users, `${TableName.OrgMembership}.userId`, `${TableName.Users}.id`) .whereNotNull(`${TableName.Membership}.actorUserId`)
.join(TableName.MembershipRole, `${TableName.Membership}.id`, `${TableName.MembershipRole}.membershipId`)
.where(`${TableName.MembershipRole}.role`, role)
.join(TableName.Users, `${TableName.Membership}.actorUserId`, `${TableName.Users}.id`)
.leftJoin<TUserEncryptionKeys>( .leftJoin<TUserEncryptionKeys>(
TableName.UserEncryptionKey, TableName.UserEncryptionKey,
`${TableName.UserEncryptionKey}.userId`, `${TableName.UserEncryptionKey}.userId`,
`${TableName.Users}.id` `${TableName.Users}.id`
) )
.select( .select(
conn.ref("id").withSchema(TableName.OrgMembership), conn.ref("id").withSchema(TableName.Membership),
conn.ref("inviteEmail").withSchema(TableName.OrgMembership), conn.ref("inviteEmail").withSchema(TableName.Membership),
conn.ref("orgId").withSchema(TableName.OrgMembership), conn.ref("orgId").withSchema(TableName.Membership),
conn.ref("role").withSchema(TableName.OrgMembership), conn.ref("role").withSchema(TableName.MembershipRole),
conn.ref("roleId").withSchema(TableName.OrgMembership), conn.ref("customRoleId").withSchema(TableName.MembershipRole).as("roleId"),
conn.ref("status").withSchema(TableName.OrgMembership), conn.ref("status").withSchema(TableName.Membership),
conn.ref("username").withSchema(TableName.Users), conn.ref("username").withSchema(TableName.Users),
conn.ref("email").withSchema(TableName.Users), conn.ref("email").withSchema(TableName.Users),
conn.ref("firstName").withSchema(TableName.Users), conn.ref("firstName").withSchema(TableName.Users),
@@ -418,47 +438,6 @@ export const orgDALFactory = (db: TDbClient) => {
} }
}; };
const findOrgGhostUser = async (orgId: string) => {
try {
const member = await db
.replicaNode()(TableName.OrgMembership)
.where({ orgId })
.join(TableName.Users, `${TableName.OrgMembership}.userId`, `${TableName.Users}.id`)
.leftJoin(TableName.UserEncryptionKey, `${TableName.UserEncryptionKey}.userId`, `${TableName.Users}.id`)
.select(
db.ref("id").withSchema(TableName.OrgMembership),
db.ref("orgId").withSchema(TableName.OrgMembership),
db.ref("role").withSchema(TableName.OrgMembership),
db.ref("roleId").withSchema(TableName.OrgMembership),
db.ref("status").withSchema(TableName.OrgMembership),
db.ref("email").withSchema(TableName.Users),
db.ref("id").withSchema(TableName.Users).as("userId"),
db.ref("publicKey").withSchema(TableName.UserEncryptionKey)
)
.where({ isGhost: true })
.first();
return member;
} catch (error) {
return null;
}
};
const ghostUserExists = async (orgId: string) => {
try {
const member = await db
.replicaNode()(TableName.OrgMembership)
.where({ orgId })
.join(TableName.Users, `${TableName.OrgMembership}.userId`, `${TableName.Users}.id`)
.leftJoin(TableName.UserEncryptionKey, `${TableName.UserEncryptionKey}.userId`, `${TableName.Users}.id`)
.select(db.ref("id").withSchema(TableName.Users).as("userId"))
.where({ isGhost: true })
.first();
return Boolean(member);
} catch (error) {
return false;
}
};
const create = async (dto: TOrganizationsInsert, tx?: Knex) => { const create = async (dto: TOrganizationsInsert, tx?: Knex) => {
try { try {
const [organization] = await (tx || db)(TableName.Organization).insert(dto).returning("*"); const [organization] = await (tx || db)(TableName.Organization).insert(dto).returning("*");
@@ -491,38 +470,37 @@ export const orgDALFactory = (db: TDbClient) => {
// MEMBERSHIP OPERATIONS // MEMBERSHIP OPERATIONS
// -------------------- // --------------------
// const orgMembershipOrm = ormify(db, TableName.OrgMembership);
const createMembership = async (data: TOrgMembershipsInsert, tx?: Knex) => { const createMembership = async (data: TMembershipsInsert, tx?: Knex) => {
try { try {
const [membership] = await (tx || db)(TableName.OrgMembership).insert(data).returning("*"); const [membership] = await (tx || db)(TableName.Membership).insert(data).returning("*");
return membership; return membership;
} catch (error) { } catch (error) {
throw new DatabaseError({ error, name: "Create org membership" }); throw new DatabaseError({ error, name: "Create org membership" });
} }
}; };
const bulkCreateMemberships = async (data: TOrgMembershipsInsert[], tx?: Knex) => { const bulkCreateMemberships = async (data: TMembershipsInsert[], tx?: Knex) => {
try { try {
const memberships = await (tx || db)(TableName.OrgMembership).insert(data).returning("*"); const memberships = await (tx || db)(TableName.Membership).insert(data).returning("*");
return memberships; return memberships;
} catch (error) { } catch (error) {
throw new DatabaseError({ error, name: "Create org memberships" }); throw new DatabaseError({ error, name: "Create org memberships" });
} }
}; };
const updateMembershipById = async (id: string, data: TOrgMembershipsUpdate, tx?: Knex) => { const updateMembershipById = async (id: string, data: TMembershipsUpdate, tx?: Knex) => {
try { try {
const [membership] = await (tx || db)(TableName.OrgMembership).where({ id }).update(data).returning("*"); const [membership] = await (tx || db)(TableName.Membership).where({ id }).update(data).returning("*");
return membership; return membership;
} catch (error) { } catch (error) {
throw new DatabaseError({ error, name: "Update org membership" }); throw new DatabaseError({ error, name: "Update org membership" });
} }
}; };
const updateMembership = async (filter: Partial<TOrgMemberships>, data: TOrgMembershipsUpdate, tx?: Knex) => { const updateMembership = async (filter: Partial<TMemberships>, data: TMembershipsUpdate, tx?: Knex) => {
try { try {
const membership = await (tx || db)(TableName.OrgMembership).where(filter).update(data).returning("*"); const membership = await (tx || db)(TableName.Membership).where(filter).update(data).returning("*");
return membership; return membership;
} catch (error) { } catch (error) {
throw new DatabaseError({ error, name: "Update org memberships" }); throw new DatabaseError({ error, name: "Update org memberships" });
@@ -531,7 +509,10 @@ export const orgDALFactory = (db: TDbClient) => {
const deleteMembershipById = async (id: string, orgId: string, tx?: Knex) => { const deleteMembershipById = async (id: string, orgId: string, tx?: Knex) => {
try { try {
const [membership] = await (tx || db)(TableName.OrgMembership).where({ id, orgId }).delete().returning("*"); const [membership] = await (tx || db)(TableName.Membership)
.where({ id, scopeOrgId: orgId, scope: AccessScope.Organization })
.delete()
.returning("*");
return membership; return membership;
} catch (error) { } catch (error) {
throw new DatabaseError({ error, name: "Delete org membership" }); throw new DatabaseError({ error, name: "Delete org membership" });
@@ -540,9 +521,10 @@ export const orgDALFactory = (db: TDbClient) => {
const deleteMembershipsById = async (ids: string[], orgId: string, tx?: Knex) => { const deleteMembershipsById = async (ids: string[], orgId: string, tx?: Knex) => {
try { try {
const memberships = await (tx || db)(TableName.OrgMembership) const memberships = await (tx || db)(TableName.Membership)
.where({ .where({
orgId scopeOrgId: orgId,
scope: AccessScope.Organization
}) })
.whereIn("id", ids) .whereIn("id", ids)
.delete() .delete()
@@ -554,22 +536,23 @@ export const orgDALFactory = (db: TDbClient) => {
}; };
const findMembership = async ( const findMembership = async (
filter: TFindFilter<TOrgMemberships>, filter: TFindFilter<TMemberships>,
{ offset, limit, sort, tx }: TFindOpt<TOrgMemberships> = {} { offset, limit, sort, tx }: TFindOpt<TMemberships> = {}
) => { ) => {
try { try {
const query = (tx || db.replicaNode())(TableName.OrgMembership) const query = (tx || db.replicaNode())(TableName.Membership)
// eslint-disable-next-line // eslint-disable-next-line
.where(buildFindFilter(filter)) .where(buildFindFilter(filter))
.join(TableName.Users, `${TableName.Users}.id`, `${TableName.OrgMembership}.userId`) .where("scope", AccessScope.Organization)
.join(TableName.Organization, `${TableName.Organization}.id`, `${TableName.OrgMembership}.orgId`) .join(TableName.Users, `${TableName.Users}.id`, `${TableName.Membership}.actorUserId`)
.join(TableName.Organization, `${TableName.Organization}.id`, `${TableName.Membership}.scopeOrgId`)
.leftJoin(TableName.UserAliases, function joinUserAlias() { .leftJoin(TableName.UserAliases, function joinUserAlias() {
this.on(`${TableName.UserAliases}.userId`, "=", `${TableName.OrgMembership}.userId`) this.on(`${TableName.UserAliases}.userId`, "=", `${TableName.Membership}.actorUserId`)
.andOn(`${TableName.UserAliases}.orgId`, "=", `${TableName.OrgMembership}.orgId`) .andOn(`${TableName.UserAliases}.orgId`, "=", `${TableName.Membership}.scopeOrgId`)
.andOn(`${TableName.UserAliases}.aliasType`, "=", (tx || db).raw("?", ["saml"])); .andOn(`${TableName.UserAliases}.aliasType`, "=", (tx || db).raw("?", ["saml"]));
}) })
.select( .select(
selectAllTableCols(TableName.OrgMembership), selectAllTableCols(TableName.Membership),
db.ref("email").withSchema(TableName.Users), db.ref("email").withSchema(TableName.Users),
db.ref("isEmailVerified").withSchema(TableName.Users), db.ref("isEmailVerified").withSchema(TableName.Users),
db.ref("username").withSchema(TableName.Users), db.ref("username").withSchema(TableName.Users),
@@ -595,18 +578,20 @@ export const orgDALFactory = (db: TDbClient) => {
const findMembershipWithScimFilter = async ( const findMembershipWithScimFilter = async (
orgId: string, orgId: string,
scimFilter: string | undefined, scimFilter: string | undefined,
{ offset, limit, sort, tx }: TFindOpt<TOrgMemberships> = {} { offset, limit, sort, tx }: TFindOpt<TMemberships> = {}
) => { ) => {
try { try {
const query = (tx || db.replicaNode())(TableName.OrgMembership) const query = (tx || db.replicaNode())(TableName.Membership)
// eslint-disable-next-line // eslint-disable-next-line
.where(`${TableName.OrgMembership}.orgId`, orgId) .where(`${TableName.Membership}.scopeOrgId`, orgId)
.where(`${TableName.Membership}.scope`, AccessScope.Organization)
.whereNotNull(`${TableName.Membership}.actorUserId`)
.where((qb) => { .where((qb) => {
if (scimFilter) { if (scimFilter) {
void generateKnexQueryFromScim(qb, scimFilter, (attrPath) => { void generateKnexQueryFromScim(qb, scimFilter, (attrPath) => {
switch (attrPath) { switch (attrPath) {
case "active": case "active":
return `${TableName.OrgMembership}.isActive`; return `${TableName.Membership}.isActive`;
case "userName": case "userName":
return `${TableName.UserAliases}.externalId`; return `${TableName.UserAliases}.externalId`;
case "name.givenName": case "name.givenName":
@@ -621,15 +606,15 @@ export const orgDALFactory = (db: TDbClient) => {
}); });
} }
}) })
.join(TableName.Users, `${TableName.Users}.id`, `${TableName.OrgMembership}.userId`) .join(TableName.Users, `${TableName.Users}.id`, `${TableName.Membership}.actorUserId`)
.join(TableName.Organization, `${TableName.Organization}.id`, `${TableName.OrgMembership}.orgId`) .join(TableName.Organization, `${TableName.Organization}.id`, `${TableName.Membership}.scopeOrgId`)
.leftJoin(TableName.UserAliases, function joinUserAlias() { .leftJoin(TableName.UserAliases, function joinUserAlias() {
this.on(`${TableName.UserAliases}.userId`, "=", `${TableName.OrgMembership}.userId`) this.on(`${TableName.UserAliases}.userId`, "=", `${TableName.Membership}.actorUserId`)
.andOn(`${TableName.UserAliases}.orgId`, "=", `${TableName.OrgMembership}.orgId`) .andOn(`${TableName.UserAliases}.orgId`, "=", `${TableName.Membership}.scopeOrgId`)
.andOn(`${TableName.UserAliases}.aliasType`, "=", (tx || db).raw("?", ["saml"])); .andOn(`${TableName.UserAliases}.aliasType`, "=", (tx || db).raw("?", ["saml"]));
}) })
.select( .select(
selectAllTableCols(TableName.OrgMembership), selectAllTableCols(TableName.Membership),
db.ref("email").withSchema(TableName.Users), db.ref("email").withSchema(TableName.Users),
db.ref("isEmailVerified").withSchema(TableName.Users), db.ref("isEmailVerified").withSchema(TableName.Users),
db.ref("username").withSchema(TableName.Users), db.ref("username").withSchema(TableName.Users),
@@ -653,18 +638,22 @@ export const orgDALFactory = (db: TDbClient) => {
} }
}; };
// TODO(simp): resolve this role field later
const findIdentityOrganization = async ( const findIdentityOrganization = async (
identityId: string identityId: string
): Promise<{ id: string; name: string; slug: string; role: string }> => { ): Promise<{ id: string; name: string; slug: string; role: string }> => {
try { try {
const org = await db const org = await db
.replicaNode()(TableName.IdentityOrgMembership) .replicaNode()(TableName.Membership)
.where({ identityId }) .where({ actorIdentityId: identityId })
.join(TableName.Organization, `${TableName.IdentityOrgMembership}.orgId`, `${TableName.Organization}.id`) .where(`${TableName.Membership}.scope`, AccessScope.Organization)
.whereNotNull(`${TableName.Membership}.actorUserId`)
.join(TableName.MembershipRole, `${TableName.Membership}.id`, `${TableName.MembershipRole}.membershipId`)
.join(TableName.Organization, `${TableName.Membership}.scopeOrgId`, `${TableName.Organization}.id`)
.select(db.ref("id").withSchema(TableName.Organization).as("id")) .select(db.ref("id").withSchema(TableName.Organization).as("id"))
.select(db.ref("name").withSchema(TableName.Organization).as("name")) .select(db.ref("name").withSchema(TableName.Organization).as("name"))
.select(db.ref("slug").withSchema(TableName.Organization).as("slug")) .select(db.ref("slug").withSchema(TableName.Organization).as("slug"))
.select(db.ref("role").withSchema(TableName.IdentityOrgMembership).as("role")); .select(db.ref("role").withSchema(TableName.MembershipRole).as("role"));
return org?.[0]; return org?.[0];
} catch (error) { } catch (error) {
@@ -681,10 +670,8 @@ export const orgDALFactory = (db: TDbClient) => {
findOrgBySlug, findOrgBySlug,
findAllOrgsByUserId, findAllOrgsByUserId,
findOrganizationsByFilter, findOrganizationsByFilter,
ghostUserExists,
findOrgMembersByUsername, findOrgMembersByUsername,
findOrgMembersByRole, findOrgMembersByRole,
findOrgGhostUser,
create, create,
updateById, updateById,
deleteById, deleteById,
-7
View File
@@ -1,7 +0,0 @@
import { TDbClient } from "@app/db";
import { TableName } from "@app/db/schemas";
import { ormify } from "@app/lib/knex";
export type TOrgRoleDALFactory = ReturnType<typeof orgRoleDALFactory>;
export const orgRoleDALFactory = (db: TDbClient) => ormify(db, TableName.OrgRoles);
+5 -4
View File
@@ -1,7 +1,8 @@
import { OrgMembershipRole } from "@app/db/schemas"; import { OrgMembershipRole } from "@app/db/schemas";
import { TFeatureSet } from "@app/ee/services/license/license-types"; import { TFeatureSet } from "@app/ee/services/license/license-types";
import { BadRequestError, NotFoundError } from "@app/lib/errors"; import { BadRequestError, NotFoundError } from "@app/lib/errors";
import { TOrgRoleDALFactory } from "@app/services/org/org-role-dal";
import { TRoleDALFactory } from "../role/role-dal";
const RESERVED_ORG_ROLE_SLUGS = Object.values(OrgMembershipRole).filter((role) => role !== "custom"); const RESERVED_ORG_ROLE_SLUGS = Object.values(OrgMembershipRole).filter((role) => role !== "custom");
@@ -10,13 +11,13 @@ export const isCustomOrgRole = (roleSlug: string) => !RESERVED_ORG_ROLE_SLUGS.fi
// this is only for updating an org // this is only for updating an org
export const getDefaultOrgMembershipRoleForUpdateOrg = async ({ export const getDefaultOrgMembershipRoleForUpdateOrg = async ({
membershipRoleSlug, membershipRoleSlug,
orgRoleDAL, roleDAL,
plan, plan,
orgId orgId
}: { }: {
orgId: string; orgId: string;
membershipRoleSlug: string; membershipRoleSlug: string;
orgRoleDAL: TOrgRoleDALFactory; roleDAL: TRoleDALFactory;
plan: TFeatureSet; plan: TFeatureSet;
}) => { }) => {
if (isCustomOrgRole(membershipRoleSlug)) { if (isCustomOrgRole(membershipRoleSlug)) {
@@ -26,7 +27,7 @@ export const getDefaultOrgMembershipRoleForUpdateOrg = async ({
"Failed to set custom default role due to plan RBAC restriction. Upgrade plan to set custom default org membership role." "Failed to set custom default role due to plan RBAC restriction. Upgrade plan to set custom default org membership role."
}); });
const customRole = await orgRoleDAL.findOne({ slug: membershipRoleSlug, orgId }); const customRole = await roleDAL.findOne({ slug: membershipRoleSlug, orgId });
if (!customRole) { if (!customRole) {
throw new NotFoundError({ throw new NotFoundError({
name: "UpdateOrg", name: "UpdateOrg",
+4 -3
View File
@@ -14,7 +14,7 @@ import { BadRequestError, NotFoundError } from "@app/lib/errors";
import { TExternalGroupOrgRoleMappingDALFactory } from "@app/services/external-group-org-role-mapping/external-group-org-role-mapping-dal"; import { TExternalGroupOrgRoleMappingDALFactory } from "@app/services/external-group-org-role-mapping/external-group-org-role-mapping-dal";
import { TOrgDALFactory } from "@app/services/org/org-dal"; import { TOrgDALFactory } from "@app/services/org/org-dal";
import { ActorAuthMethod } from "../auth/auth-type"; import { ActorAuthMethod, ActorType } from "../auth/auth-type";
import { TOrgRoleDALFactory } from "./org-role-dal"; import { TOrgRoleDALFactory } from "./org-role-dal";
type TOrgRoleServiceFactoryDep = { type TOrgRoleServiceFactoryDep = {
@@ -224,13 +224,14 @@ export const orgRoleServiceFactory = ({
actorAuthMethod: ActorAuthMethod, actorAuthMethod: ActorAuthMethod,
actorOrgId: string | undefined actorOrgId: string | undefined
) => { ) => {
const { permission, membership } = await permissionService.getUserOrgPermission( const { permission, memberships } = await permissionService.getOrgPermission(
ActorType.USER,
userId, userId,
orgId, orgId,
actorAuthMethod, actorAuthMethod,
actorOrgId actorOrgId
); );
return { permissions: packRules(permission.rules), membership }; return { permissions: packRules(permission.rules), memberships };
}; };
return { createRole, getRole, updateRole, deleteRole, listRoles, getUserPermission }; return { createRole, getRole, updateRole, deleteRole, listRoles, getUserPermission };
+39 -15
View File
@@ -76,7 +76,6 @@ import { TIncidentContactsDALFactory } from "./incident-contacts-dal";
import { TOrgBotDALFactory } from "./org-bot-dal"; import { TOrgBotDALFactory } from "./org-bot-dal";
import { TOrgDALFactory } from "./org-dal"; import { TOrgDALFactory } from "./org-dal";
import { deleteOrgMembershipFn, deleteOrgMembershipsFn } from "./org-fns"; import { deleteOrgMembershipFn, deleteOrgMembershipsFn } from "./org-fns";
import { TOrgRoleDALFactory } from "./org-role-dal";
import { import {
TDeleteOrgMembershipDTO, TDeleteOrgMembershipDTO,
TDeleteOrgMembershipsDTO, TDeleteOrgMembershipsDTO,
@@ -92,6 +91,7 @@ import {
TUpgradePrivilegeSystemDTO, TUpgradePrivilegeSystemDTO,
TVerifyUserToOrgDTO TVerifyUserToOrgDTO
} from "./org-types"; } from "./org-types";
import { TRoleDALFactory } from "../role/role-dal";
type TOrgServiceFactoryDep = { type TOrgServiceFactoryDep = {
userAliasDAL: Pick<TUserAliasDALFactory, "delete">; userAliasDAL: Pick<TUserAliasDALFactory, "delete">;
@@ -100,7 +100,7 @@ type TOrgServiceFactoryDep = {
folderDAL: Pick<TSecretFolderDALFactory, "findByProjectId">; folderDAL: Pick<TSecretFolderDALFactory, "findByProjectId">;
orgDAL: TOrgDALFactory; orgDAL: TOrgDALFactory;
orgBotDAL: TOrgBotDALFactory; orgBotDAL: TOrgBotDALFactory;
orgRoleDAL: TOrgRoleDALFactory; roleDAL: TRoleDALFactory;
userDAL: TUserDALFactory; userDAL: TUserDALFactory;
groupDAL: TGroupDALFactory; groupDAL: TGroupDALFactory;
projectDAL: TProjectDALFactory; projectDAL: TProjectDALFactory;
@@ -154,7 +154,7 @@ export const orgServiceFactory = ({
folderDAL, folderDAL,
userDAL, userDAL,
groupDAL, groupDAL,
orgRoleDAL, roleDAL,
incidentContactDAL, incidentContactDAL,
permissionService, permissionService,
smtpService, smtpService,
@@ -185,7 +185,7 @@ export const orgServiceFactory = ({
actorAuthMethod: ActorAuthMethod, actorAuthMethod: ActorAuthMethod,
actorOrgId: string | undefined actorOrgId: string | undefined
) => { ) => {
await permissionService.getUserOrgPermission(userId, orgId, actorAuthMethod, actorOrgId); await permissionService.getOrgPermission(ActorType.USER, userId, orgId, actorAuthMethod, actorOrgId);
const appCfg = getConfig(); const appCfg = getConfig();
const org = await orgDAL.findOrgById(orgId); const org = await orgDAL.findOrgById(orgId);
if (!org) throw new NotFoundError({ message: `Organization with ID '${orgId}' not found` }); if (!org) throw new NotFoundError({ message: `Organization with ID '${orgId}' not found` });
@@ -221,7 +221,13 @@ export const orgServiceFactory = ({
actorAuthMethod: ActorAuthMethod, actorAuthMethod: ActorAuthMethod,
actorOrgId: string | undefined actorOrgId: string | undefined
) => { ) => {
const { permission } = await permissionService.getUserOrgPermission(userId, orgId, actorAuthMethod, actorOrgId); const { permission } = await permissionService.getOrgPermission(
ActorType.USER,
userId,
orgId,
actorAuthMethod,
actorOrgId
);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Member); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Member);
const members = await orgDAL.findAllOrgMembers(orgId); const members = await orgDAL.findAllOrgMembers(orgId);
@@ -323,9 +329,15 @@ export const orgServiceFactory = ({
actorAuthMethod, actorAuthMethod,
orgId orgId
}: TUpgradePrivilegeSystemDTO) => { }: TUpgradePrivilegeSystemDTO) => {
const { membership } = await permissionService.getUserOrgPermission(actorId, orgId, actorAuthMethod, actorOrgId); const { hasRole } = await permissionService.getOrgPermission(
ActorType.USER,
actorId,
orgId,
actorAuthMethod,
actorOrgId
);
if (membership.role !== OrgMembershipRole.Admin) { if (!hasRole(OrgMembershipRole.Admin)) {
throw new ForbiddenRequestError({ throw new ForbiddenRequestError({
message: "Insufficient privileges - only the organization admin can upgrade the privilege system." message: "Insufficient privileges - only the organization admin can upgrade the privilege system."
}); });
@@ -531,7 +543,7 @@ export const orgServiceFactory = ({
defaultMembershipRole = await getDefaultOrgMembershipRoleForUpdateOrg({ defaultMembershipRole = await getDefaultOrgMembershipRoleForUpdateOrg({
membershipRoleSlug: defaultMembershipRoleSlug, membershipRoleSlug: defaultMembershipRoleSlug,
orgId, orgId,
orgRoleDAL, roleDAL,
plan plan
}); });
} }
@@ -659,8 +671,14 @@ export const orgServiceFactory = ({
actorAuthMethod: ActorAuthMethod; actorAuthMethod: ActorAuthMethod;
actorOrgId: string | undefined; actorOrgId: string | undefined;
}) => { }) => {
const { membership } = await permissionService.getUserOrgPermission(userId, orgId, actorAuthMethod, actorOrgId); const { hasRole } = await permissionService.getOrgPermission(
if ((membership.role as OrgMembershipRole) !== OrgMembershipRole.Admin) { ActorType.USER,
userId,
orgId,
actorAuthMethod,
actorOrgId
);
if (!hasRole(OrgMembershipRole.Admin)) {
throw new ForbiddenRequestError({ throw new ForbiddenRequestError({
name: "DeleteOrganizationById", name: "DeleteOrganizationById",
message: "Insufficient privileges" message: "Insufficient privileges"
@@ -739,7 +757,13 @@ export const orgServiceFactory = ({
actorOrgId, actorOrgId,
metadata metadata
}: TUpdateOrgMembershipDTO) => { }: TUpdateOrgMembershipDTO) => {
const { permission } = await permissionService.getUserOrgPermission(userId, orgId, actorAuthMethod, actorOrgId); const { permission } = await permissionService.getOrgPermission(
ActorType.USER,
userId,
orgId,
actorAuthMethod,
actorOrgId
);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Member); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Member);
const foundMembership = await orgMembershipDAL.findById(membershipId); const foundMembership = await orgMembershipDAL.findById(membershipId);
@@ -754,7 +778,7 @@ export const orgServiceFactory = ({
let userRole = role; let userRole = role;
let userRoleId: string | null = null; let userRoleId: string | null = null;
if (role && isCustomRole) { if (role && isCustomRole) {
const customRole = await orgRoleDAL.findOne({ slug: role, orgId }); const customRole = await roleDAL.findOne({ slug: role, orgId });
if (!customRole) throw new BadRequestError({ name: "UpdateMembership", message: "Organization role not found" }); if (!customRole) throw new BadRequestError({ name: "UpdateMembership", message: "Organization role not found" });
const plan = await licenseService.getPlan(orgId); const plan = await licenseService.getPlan(orgId);
@@ -768,16 +792,16 @@ export const orgServiceFactory = ({
} }
const membership = await orgDAL.transaction(async (tx) => { const membership = await orgDAL.transaction(async (tx) => {
const [updatedOrgMembership] = await orgDAL.updateMembership( const [updatedOrgMembership] = await orgDAL.updateMembership(
{ id: membershipId, orgId }, { id: membershipId, scopeOrgId: orgId },
{ role: userRole, roleId: userRoleId, isActive } { role: userRole, roleId: userRoleId, isActive }
); );
if (metadata) { if (metadata) {
await identityMetadataDAL.delete({ userId: updatedOrgMembership.userId, orgId }, tx); await identityMetadataDAL.delete({ userId: updatedOrgMembership.actorUserId, orgId }, tx);
if (metadata.length) { if (metadata.length) {
await identityMetadataDAL.insertMany( await identityMetadataDAL.insertMany(
metadata.map(({ key, value }) => ({ metadata.map(({ key, value }) => ({
userId: updatedOrgMembership.userId, userId: updatedOrgMembership.actorUserId as string,
orgId, orgId,
key, key,
value value
@@ -1,7 +1,7 @@
import { Knex } from "knex"; import { Knex } from "knex";
import { TDbClient } from "@app/db"; import { TDbClient } from "@app/db";
import { TableName, TProjectBots, TUserEncryptionKeys } from "@app/db/schemas"; import { AccessScope, TableName, TProjectBots, TUserEncryptionKeys } from "@app/db/schemas";
import { DatabaseError } from "@app/lib/errors"; import { DatabaseError } from "@app/lib/errors";
import { ormify, selectAllTableCols } from "@app/lib/knex"; import { ormify, selectAllTableCols } from "@app/lib/knex";
@@ -44,12 +44,13 @@ export const projectBotDALFactory = (db: TDbClient) => {
const findProjectUserWorkspaceKey = async (projectId: string) => { const findProjectUserWorkspaceKey = async (projectId: string) => {
try { try {
const doc = await db const doc = await db
.replicaNode()(TableName.ProjectMembership) .replicaNode()(TableName.Membership)
.where(`${TableName.ProjectMembership}.projectId` as "projectId", projectId) .where(`${TableName.Membership}.scopeProjectId` as "projectId", projectId)
.where(`${TableName.ProjectKeys}.projectId` as "projectId", projectId) .where(`${TableName.Membership}.scope`, AccessScope.Project)
.where(`${TableName.Users}.isGhost` as "isGhost", false) .where(`${TableName.Users}.isGhost` as "isGhost", false)
.join(TableName.Users, `${TableName.ProjectMembership}.userId`, `${TableName.Users}.id`) .join(TableName.Users, `${TableName.Membership}.actorUserId`, `${TableName.Users}.id`)
.join(TableName.ProjectKeys, `${TableName.ProjectMembership}.userId`, `${TableName.ProjectKeys}.receiverId`) .join(TableName.ProjectKeys, `${TableName.Membership}.actorUserId`, `${TableName.ProjectKeys}.receiverId`)
.where(`${TableName.ProjectKeys}.projectId` as "projectId", projectId)
.join<TUserEncryptionKeys>( .join<TUserEncryptionKeys>(
TableName.UserEncryptionKey, TableName.UserEncryptionKey,
`${TableName.UserEncryptionKey}.userId`, `${TableName.UserEncryptionKey}.userId`,
@@ -1,7 +1,7 @@
import { Knex } from "knex"; import { Knex } from "knex";
import { TDbClient } from "@app/db"; import { TDbClient } from "@app/db";
import { TableName, TProjectKeys } from "@app/db/schemas"; import { AccessScope, TableName, TProjectKeys } from "@app/db/schemas";
import { DatabaseError } from "@app/lib/errors"; import { DatabaseError } from "@app/lib/errors";
import { ormify, selectAllTableCols } from "@app/lib/knex"; import { ormify, selectAllTableCols } from "@app/lib/knex";
@@ -34,9 +34,10 @@ export const projectKeyDALFactory = (db: TDbClient) => {
const findAllProjectUserPubKeys = async (projectId: string, tx?: Knex) => { const findAllProjectUserPubKeys = async (projectId: string, tx?: Knex) => {
try { try {
const pubKeys = await (tx || db.replicaNode())(TableName.ProjectMembership) const pubKeys = await (tx || db.replicaNode())(TableName.Membership)
.where({ projectId }) .where(`${TableName.Membership}.scopeProjectId` as "projectId", projectId)
.join(TableName.Users, `${TableName.ProjectMembership}.userId`, `${TableName.Users}.id`) .where(`${TableName.Membership}.scope`, AccessScope.Project)
.join(TableName.Users, `${TableName.Membership}.actorUserId`, `${TableName.Users}.id`)
.join(TableName.UserEncryptionKey, `${TableName.Users}.id`, `${TableName.UserEncryptionKey}.userId`) .join(TableName.UserEncryptionKey, `${TableName.Users}.id`, `${TableName.UserEncryptionKey}.userId`)
.select("userId", "publicKey"); .select("userId", "publicKey");
return pubKeys; return pubKeys;
@@ -1,15 +1,13 @@
import { Knex } from "knex"; import { Knex } from "knex";
import { TDbClient } from "@app/db"; import { TDbClient } from "@app/db";
import { TableName, TUserEncryptionKeys } from "@app/db/schemas"; import { AccessScope, TableName, TMemberships, TUserEncryptionKeys } from "@app/db/schemas";
import { DatabaseError } from "@app/lib/errors"; import { DatabaseError } from "@app/lib/errors";
import { ormify, selectAllTableCols, sqlNestRelationships } from "@app/lib/knex"; import { selectAllTableCols, sqlNestRelationships } from "@app/lib/knex";
export type TProjectMembershipDALFactory = ReturnType<typeof projectMembershipDALFactory>; export type TProjectMembershipDALFactory = ReturnType<typeof projectMembershipDALFactory>;
export const projectMembershipDALFactory = (db: TDbClient) => { export const projectMembershipDALFactory = (db: TDbClient) => {
const projectMemberOrm = ormify(db, TableName.ProjectMembership);
// special query // special query
const findAllProjectMembers = async ( const findAllProjectMembers = async (
projectId: string, projectId: string,
@@ -17,18 +15,17 @@ export const projectMembershipDALFactory = (db: TDbClient) => {
) => { ) => {
try { try {
const docs = await db const docs = await db
.replicaNode()(TableName.ProjectMembership) .replicaNode()(TableName.Membership)
.where({ [`${TableName.ProjectMembership}.projectId` as "projectId"]: projectId }) .where({ [`${TableName.Membership}.scopeProjectId` as "scopeProjectId"]: projectId })
.join(TableName.Project, `${TableName.ProjectMembership}.projectId`, `${TableName.Project}.id`) .where({ [`${TableName.Membership}.scope` as "scope"]: AccessScope.Project })
.join(TableName.Users, `${TableName.ProjectMembership}.userId`, `${TableName.Users}.id`) .whereNotNull(`${TableName.Membership}.actorUserId`)
.join(TableName.OrgMembership, (qb) => { .join(TableName.Project, `${TableName.Membership}.scopeProjectId`, `${TableName.Project}.id`)
qb.on(`${TableName.Users}.id`, "=", `${TableName.OrgMembership}.userId`).andOn( .join(TableName.Users, `${TableName.Membership}.actorUserId`, `${TableName.Users}.id`)
`${TableName.OrgMembership}.orgId`, .join<TMemberships>(db(TableName.Membership).as("orgMembership"), (qb) => {
"=", qb.on(`${TableName.Users}.id`, "=", `orgMembership.actorUserId`)
`${TableName.Project}.orgId` .andOn(`orgMembership.scopeOrgId`, "=", `${TableName.Project}.orgId`)
); .andOn("orgMembership.scope", AccessScope.Organization);
}) })
.where((qb) => { .where((qb) => {
if (filter.usernames) { if (filter.usernames) {
void qb.whereIn("username", filter.usernames); void qb.whereIn("username", filter.usernames);
@@ -37,28 +34,24 @@ export const projectMembershipDALFactory = (db: TDbClient) => {
void qb.where("username", filter.username); void qb.where("username", filter.username);
} }
if (filter.id) { if (filter.id) {
void qb.where(`${TableName.ProjectMembership}.id`, filter.id); void qb.where(`${TableName.Membership}.id`, filter.id);
} }
if (filter.roles && filter.roles.length > 0) { if (filter.roles && filter.roles.length > 0) {
void qb.whereExists((subQuery) => { void qb.whereExists((subQuery) => {
void subQuery void subQuery
.select("role") .select("role")
.from(TableName.ProjectUserMembershipRole) .from(TableName.MembershipRole)
.leftJoin( .leftJoin(TableName.Role, `${TableName.Role}.id`, `${TableName.MembershipRole}.customRoleId`)
TableName.ProjectRoles,
`${TableName.ProjectRoles}.id`,
`${TableName.ProjectUserMembershipRole}.customRoleId`
)
.whereRaw("??.?? = ??.??", [ .whereRaw("??.?? = ??.??", [
TableName.ProjectUserMembershipRole, TableName.MembershipRole,
"projectMembershipId", "projectMembershipId",
TableName.ProjectMembership, TableName.Membership,
"id" "id"
]) ])
.where((subQb) => { .where((subQb) => {
void subQb void subQb
.whereIn(`${TableName.ProjectUserMembershipRole}.role`, filter.roles as string[]) .whereIn(`${TableName.MembershipRole}.role`, filter.roles as string[])
.orWhereIn(`${TableName.ProjectRoles}.slug`, filter.roles as string[]); .orWhereIn(`${TableName.Role}.slug`, filter.roles as string[]);
}); });
}); });
} }
@@ -68,19 +61,11 @@ export const projectMembershipDALFactory = (db: TDbClient) => {
`${TableName.UserEncryptionKey}.userId`, `${TableName.UserEncryptionKey}.userId`,
`${TableName.Users}.id` `${TableName.Users}.id`
) )
.join( .join(TableName.MembershipRole, `${TableName.MembershipRole}.membershipId`, `${TableName.Membership}.id`)
TableName.ProjectUserMembershipRole, .leftJoin(TableName.Role, `${TableName.MembershipRole}.customRoleId`, `${TableName.Role}.id`)
`${TableName.ProjectUserMembershipRole}.projectMembershipId`,
`${TableName.ProjectMembership}.id`
)
.leftJoin(
TableName.ProjectRoles,
`${TableName.ProjectUserMembershipRole}.customRoleId`,
`${TableName.ProjectRoles}.id`
)
.select( .select(
db.ref("id").withSchema(TableName.ProjectMembership), db.ref("id").withSchema(TableName.Membership),
db.ref("createdAt").withSchema(TableName.ProjectMembership), db.ref("createdAt").withSchema(TableName.Membership),
db.ref("isGhost").withSchema(TableName.Users), db.ref("isGhost").withSchema(TableName.Users),
db.ref("username").withSchema(TableName.Users), db.ref("username").withSchema(TableName.Users),
db.ref("email").withSchema(TableName.Users), db.ref("email").withSchema(TableName.Users),
@@ -88,18 +73,18 @@ export const projectMembershipDALFactory = (db: TDbClient) => {
db.ref("firstName").withSchema(TableName.Users), db.ref("firstName").withSchema(TableName.Users),
db.ref("lastName").withSchema(TableName.Users), db.ref("lastName").withSchema(TableName.Users),
db.ref("id").withSchema(TableName.Users).as("userId"), db.ref("id").withSchema(TableName.Users).as("userId"),
db.ref("role").withSchema(TableName.ProjectUserMembershipRole), db.ref("role").withSchema(TableName.MembershipRole),
db.ref("id").withSchema(TableName.ProjectUserMembershipRole).as("membershipRoleId"), db.ref("id").withSchema(TableName.MembershipRole).as("membershipRoleId"),
db.ref("customRoleId").withSchema(TableName.ProjectUserMembershipRole), db.ref("customRoleId").withSchema(TableName.MembershipRole),
db.ref("name").withSchema(TableName.ProjectRoles).as("customRoleName"), db.ref("name").withSchema(TableName.Role).as("customRoleName"),
db.ref("slug").withSchema(TableName.ProjectRoles).as("customRoleSlug"), db.ref("slug").withSchema(TableName.Role).as("customRoleSlug"),
db.ref("temporaryMode").withSchema(TableName.ProjectUserMembershipRole), db.ref("temporaryMode").withSchema(TableName.MembershipRole),
db.ref("isTemporary").withSchema(TableName.ProjectUserMembershipRole), db.ref("isTemporary").withSchema(TableName.MembershipRole),
db.ref("temporaryRange").withSchema(TableName.ProjectUserMembershipRole), db.ref("temporaryRange").withSchema(TableName.MembershipRole),
db.ref("temporaryAccessStartTime").withSchema(TableName.ProjectUserMembershipRole), db.ref("temporaryAccessStartTime").withSchema(TableName.MembershipRole),
db.ref("temporaryAccessEndTime").withSchema(TableName.ProjectUserMembershipRole), db.ref("temporaryAccessEndTime").withSchema(TableName.MembershipRole),
db.ref("name").as("projectName").withSchema(TableName.Project), db.ref("name").as("projectName").withSchema(TableName.Project),
db.ref("isActive").withSchema(TableName.OrgMembership) db.ref("isActive").withSchema("orgMembership")
) )
.where({ isGhost: false }) .where({ isGhost: false })
.orderBy(`${TableName.Users}.username` as "username"); .orderBy(`${TableName.Users}.username` as "username");
@@ -184,9 +169,11 @@ export const projectMembershipDALFactory = (db: TDbClient) => {
const findProjectGhostUser = async (projectId: string, tx?: Knex) => { const findProjectGhostUser = async (projectId: string, tx?: Knex) => {
try { try {
const ghostUser = await (tx || db.replicaNode())(TableName.ProjectMembership) const ghostUser = await (tx || db.replicaNode())(TableName.Membership)
.where({ projectId }) .where({ [`${TableName.Membership}.scopeProjectId` as "scopeProjectId"]: projectId })
.join(TableName.Users, `${TableName.ProjectMembership}.userId`, `${TableName.Users}.id`) .where({ [`${TableName.Membership}.scope` as "scope"]: AccessScope.Project })
.whereNotNull(`${TableName.Membership}.actorUserId`)
.join(TableName.Users, `${TableName.Membership}.actorUserId`, `${TableName.Users}.id`)
.select(selectAllTableCols(TableName.Users)) .select(selectAllTableCols(TableName.Users))
.where({ isGhost: true }) .where({ isGhost: true })
.first(); .first();
@@ -200,21 +187,24 @@ export const projectMembershipDALFactory = (db: TDbClient) => {
const findMembershipsByUsername = async (projectId: string, usernames: string[]) => { const findMembershipsByUsername = async (projectId: string, usernames: string[]) => {
try { try {
const members = await db const members = await db
.replicaNode()(TableName.ProjectMembership) .replicaNode()(TableName.Membership)
.where({ projectId }) .where({ [`${TableName.Membership}.scopeProjectId` as "scopeProjectId"]: projectId })
.join(TableName.Users, `${TableName.ProjectMembership}.userId`, `${TableName.Users}.id`) .where({ [`${TableName.Membership}.scope` as "scope"]: AccessScope.Project })
.whereNotNull(`${TableName.Membership}.actorUserId`)
.join(TableName.Users, `${TableName.Membership}.actorUserId`, `${TableName.Users}.id`)
.join<TUserEncryptionKeys>( .join<TUserEncryptionKeys>(
TableName.UserEncryptionKey, TableName.UserEncryptionKey,
`${TableName.UserEncryptionKey}.userId`, `${TableName.UserEncryptionKey}.userId`,
`${TableName.Users}.id` `${TableName.Users}.id`
) )
.select( .select(
selectAllTableCols(TableName.ProjectMembership), selectAllTableCols(TableName.Membership),
db.ref("id").withSchema(TableName.Users).as("userId"), db.ref("id").withSchema(TableName.Users).as("userId"),
db.ref("username").withSchema(TableName.Users) db.ref("username").withSchema(TableName.Users)
) )
.whereIn("username", usernames) .whereIn("username", usernames)
.where({ isGhost: false }); .where({ isGhost: false });
return members.map(({ userId, username, ...data }) => ({ return members.map(({ userId, username, ...data }) => ({
...data, ...data,
user: { id: userId, username } user: { id: userId, username }
@@ -227,9 +217,11 @@ export const projectMembershipDALFactory = (db: TDbClient) => {
const findProjectMembershipsByUserId = async (orgId: string, userId: string) => { const findProjectMembershipsByUserId = async (orgId: string, userId: string) => {
try { try {
const docs = await db const docs = await db
.replicaNode()(TableName.ProjectMembership) .replicaNode()(TableName.Membership)
.join(TableName.Project, `${TableName.ProjectMembership}.projectId`, `${TableName.Project}.id`) .where({ [`${TableName.Membership}.scope` as "scope"]: AccessScope.Project })
.join(TableName.Users, `${TableName.ProjectMembership}.userId`, `${TableName.Users}.id`) .whereNotNull(`${TableName.Membership}.actorUserId`)
.join(TableName.Project, `${TableName.Membership}.scopeProjectId`, `${TableName.Project}.id`)
.join(TableName.Users, `${TableName.Membership}.actorUserId`, `${TableName.Users}.id`)
.where(`${TableName.Users}.id`, userId) .where(`${TableName.Users}.id`, userId)
.where(`${TableName.Project}.orgId`, orgId) .where(`${TableName.Project}.orgId`, orgId)
.join<TUserEncryptionKeys>( .join<TUserEncryptionKeys>(
@@ -237,18 +229,10 @@ export const projectMembershipDALFactory = (db: TDbClient) => {
`${TableName.UserEncryptionKey}.userId`, `${TableName.UserEncryptionKey}.userId`,
`${TableName.Users}.id` `${TableName.Users}.id`
) )
.join( .join(TableName.MembershipRole, `${TableName.MembershipRole}.membershipId`, `${TableName.Membership}.id`)
TableName.ProjectUserMembershipRole, .leftJoin(TableName.Role, `${TableName.MembershipRole}.customRoleId`, `${TableName.Role}.id`)
`${TableName.ProjectUserMembershipRole}.projectMembershipId`,
`${TableName.ProjectMembership}.id`
)
.leftJoin(
TableName.ProjectRoles,
`${TableName.ProjectUserMembershipRole}.customRoleId`,
`${TableName.ProjectRoles}.id`
)
.select( .select(
db.ref("id").withSchema(TableName.ProjectMembership), db.ref("id").withSchema(TableName.Membership),
db.ref("isGhost").withSchema(TableName.Users), db.ref("isGhost").withSchema(TableName.Users),
db.ref("username").withSchema(TableName.Users), db.ref("username").withSchema(TableName.Users),
db.ref("email").withSchema(TableName.Users), db.ref("email").withSchema(TableName.Users),
@@ -256,16 +240,16 @@ export const projectMembershipDALFactory = (db: TDbClient) => {
db.ref("firstName").withSchema(TableName.Users), db.ref("firstName").withSchema(TableName.Users),
db.ref("lastName").withSchema(TableName.Users), db.ref("lastName").withSchema(TableName.Users),
db.ref("id").withSchema(TableName.Users).as("userId"), db.ref("id").withSchema(TableName.Users).as("userId"),
db.ref("role").withSchema(TableName.ProjectUserMembershipRole), db.ref("role").withSchema(TableName.MembershipRole),
db.ref("id").withSchema(TableName.ProjectUserMembershipRole).as("membershipRoleId"), db.ref("id").withSchema(TableName.MembershipRole).as("membershipRoleId"),
db.ref("customRoleId").withSchema(TableName.ProjectUserMembershipRole), db.ref("customRoleId").withSchema(TableName.MembershipRole),
db.ref("name").withSchema(TableName.ProjectRoles).as("customRoleName"), db.ref("name").withSchema(TableName.Role).as("customRoleName"),
db.ref("slug").withSchema(TableName.ProjectRoles).as("customRoleSlug"), db.ref("slug").withSchema(TableName.Role).as("customRoleSlug"),
db.ref("temporaryMode").withSchema(TableName.ProjectUserMembershipRole), db.ref("temporaryMode").withSchema(TableName.MembershipRole),
db.ref("isTemporary").withSchema(TableName.ProjectUserMembershipRole), db.ref("isTemporary").withSchema(TableName.MembershipRole),
db.ref("temporaryRange").withSchema(TableName.ProjectUserMembershipRole), db.ref("temporaryRange").withSchema(TableName.MembershipRole),
db.ref("temporaryAccessStartTime").withSchema(TableName.ProjectUserMembershipRole), db.ref("temporaryAccessStartTime").withSchema(TableName.MembershipRole),
db.ref("temporaryAccessEndTime").withSchema(TableName.ProjectUserMembershipRole), db.ref("temporaryAccessEndTime").withSchema(TableName.MembershipRole),
db.ref("name").as("projectName").withSchema(TableName.Project), db.ref("name").as("projectName").withSchema(TableName.Project),
db.ref("id").as("projectId").withSchema(TableName.Project), db.ref("id").as("projectId").withSchema(TableName.Project),
db.ref("type").as("projectType").withSchema(TableName.Project) db.ref("type").as("projectType").withSchema(TableName.Project)
@@ -336,9 +320,11 @@ export const projectMembershipDALFactory = (db: TDbClient) => {
const findProjectMembershipsByUserIds = async (orgId: string, userIds: string[]) => { const findProjectMembershipsByUserIds = async (orgId: string, userIds: string[]) => {
try { try {
const docs = await db const docs = await db
.replicaNode()(TableName.ProjectMembership) .replicaNode()(TableName.Membership)
.join(TableName.Project, `${TableName.ProjectMembership}.projectId`, `${TableName.Project}.id`) .where({ [`${TableName.Membership}.scope` as "scope"]: AccessScope.Project })
.join(TableName.Users, `${TableName.ProjectMembership}.userId`, `${TableName.Users}.id`) .whereNotNull(`${TableName.Membership}.actorUserId`)
.join(TableName.Project, `${TableName.Membership}.scopeProjectId`, `${TableName.Project}.id`)
.join(TableName.Users, `${TableName.Membership}.actorUserId`, `${TableName.Users}.id`)
.whereIn(`${TableName.Users}.id`, userIds) .whereIn(`${TableName.Users}.id`, userIds)
.where(`${TableName.Project}.orgId`, orgId) .where(`${TableName.Project}.orgId`, orgId)
.join<TUserEncryptionKeys>( .join<TUserEncryptionKeys>(
@@ -346,18 +332,10 @@ export const projectMembershipDALFactory = (db: TDbClient) => {
`${TableName.UserEncryptionKey}.userId`, `${TableName.UserEncryptionKey}.userId`,
`${TableName.Users}.id` `${TableName.Users}.id`
) )
.join( .join(TableName.MembershipRole, `${TableName.MembershipRole}.membershipId`, `${TableName.Membership}.id`)
TableName.ProjectUserMembershipRole, .leftJoin(TableName.Role, `${TableName.MembershipRole}.customRoleId`, `${TableName.Role}.id`)
`${TableName.ProjectUserMembershipRole}.projectMembershipId`,
`${TableName.ProjectMembership}.id`
)
.leftJoin(
TableName.ProjectRoles,
`${TableName.ProjectUserMembershipRole}.customRoleId`,
`${TableName.ProjectRoles}.id`
)
.select( .select(
db.ref("id").withSchema(TableName.ProjectMembership), db.ref("id").withSchema(TableName.Membership),
db.ref("isGhost").withSchema(TableName.Users), db.ref("isGhost").withSchema(TableName.Users),
db.ref("username").withSchema(TableName.Users), db.ref("username").withSchema(TableName.Users),
db.ref("email").withSchema(TableName.Users), db.ref("email").withSchema(TableName.Users),
@@ -365,16 +343,16 @@ export const projectMembershipDALFactory = (db: TDbClient) => {
db.ref("firstName").withSchema(TableName.Users), db.ref("firstName").withSchema(TableName.Users),
db.ref("lastName").withSchema(TableName.Users), db.ref("lastName").withSchema(TableName.Users),
db.ref("id").withSchema(TableName.Users).as("userId"), db.ref("id").withSchema(TableName.Users).as("userId"),
db.ref("role").withSchema(TableName.ProjectUserMembershipRole), db.ref("role").withSchema(TableName.MembershipRole),
db.ref("id").withSchema(TableName.ProjectUserMembershipRole).as("membershipRoleId"), db.ref("id").withSchema(TableName.MembershipRole).as("membershipRoleId"),
db.ref("customRoleId").withSchema(TableName.ProjectUserMembershipRole), db.ref("customRoleId").withSchema(TableName.MembershipRole),
db.ref("name").withSchema(TableName.ProjectRoles).as("customRoleName"), db.ref("name").withSchema(TableName.Role).as("customRoleName"),
db.ref("slug").withSchema(TableName.ProjectRoles).as("customRoleSlug"), db.ref("slug").withSchema(TableName.Role).as("customRoleSlug"),
db.ref("temporaryMode").withSchema(TableName.ProjectUserMembershipRole), db.ref("temporaryMode").withSchema(TableName.MembershipRole),
db.ref("isTemporary").withSchema(TableName.ProjectUserMembershipRole), db.ref("isTemporary").withSchema(TableName.MembershipRole),
db.ref("temporaryRange").withSchema(TableName.ProjectUserMembershipRole), db.ref("temporaryRange").withSchema(TableName.MembershipRole),
db.ref("temporaryAccessStartTime").withSchema(TableName.ProjectUserMembershipRole), db.ref("temporaryAccessStartTime").withSchema(TableName.MembershipRole),
db.ref("temporaryAccessEndTime").withSchema(TableName.ProjectUserMembershipRole), db.ref("temporaryAccessEndTime").withSchema(TableName.MembershipRole),
db.ref("name").as("projectName").withSchema(TableName.Project), db.ref("name").as("projectName").withSchema(TableName.Project),
db.ref("id").as("projectId").withSchema(TableName.Project), db.ref("id").as("projectId").withSchema(TableName.Project),
db.ref("type").as("projectType").withSchema(TableName.Project) db.ref("type").as("projectType").withSchema(TableName.Project)
@@ -444,7 +422,6 @@ export const projectMembershipDALFactory = (db: TDbClient) => {
}; };
return { return {
...projectMemberOrm,
findAllProjectMembers, findAllProjectMembers,
findProjectGhostUser, findProjectGhostUser,
findMembershipsByUsername, findMembershipsByUsername,
@@ -1,7 +1,7 @@
/* eslint-disable no-await-in-loop */ /* eslint-disable no-await-in-loop */
import { ForbiddenError } from "@casl/ability"; import { ForbiddenError } from "@casl/ability";
import { ActionProjectType, ProjectMembershipRole, ProjectVersion, TableName } from "@app/db/schemas"; import { AccessScope, ActionProjectType, ProjectMembershipRole, ProjectVersion, TableName } from "@app/db/schemas";
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service"; import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
import { import {
constructPermissionErrorMessage, constructPermissionErrorMessage,
@@ -41,21 +41,25 @@ import {
TUpdateProjectMembershipDTO TUpdateProjectMembershipDTO
} from "./project-membership-types"; } from "./project-membership-types";
import { TProjectUserMembershipRoleDALFactory } from "./project-user-membership-role-dal"; import { TProjectUserMembershipRoleDALFactory } from "./project-user-membership-role-dal";
import { TRoleDALFactory } from "../role/role-dal";
import { TAdditionalPrivilegeDALFactory } from "../additional-privilege/additional-privilege-dal";
import { TMembershipDALFactory } from "../membership/membership-dal";
import { TMembershipRoleDALFactory } from "../membership/membership-role-dal";
type TProjectMembershipServiceFactoryDep = { type TProjectMembershipServiceFactoryDep = {
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission" | "getProjectPermissionByRoles"| "invalidateProjectPermissionCache">; permissionService: Pick<TPermissionServiceFactory, "getProjectPermission" | "getProjectPermissionByRoles"| "invalidateProjectPermissionCache">;
smtpService: TSmtpService; smtpService: TSmtpService;
projectBotDAL: TProjectBotDALFactory;
projectMembershipDAL: TProjectMembershipDALFactory; projectMembershipDAL: TProjectMembershipDALFactory;
projectUserMembershipRoleDAL: Pick<TProjectUserMembershipRoleDALFactory, "insertMany" | "find" | "delete">; membershipDAL: TMembershipDALFactory;
membershipRoleDAL: Pick<TMembershipRoleDALFactory, "insertMany" | "find" | "delete">;
userDAL: Pick<TUserDALFactory, "findById" | "findOne" | "findUserByProjectMembershipId" | "find">; userDAL: Pick<TUserDALFactory, "findById" | "findOne" | "findUserByProjectMembershipId" | "find">;
userGroupMembershipDAL: TUserGroupMembershipDALFactory; userGroupMembershipDAL: TUserGroupMembershipDALFactory;
projectRoleDAL: Pick<TProjectRoleDALFactory, "find" | "findOne">; roleDAL: Pick<TRoleDALFactory, "find" | "findOne">;
orgDAL: Pick<TOrgDALFactory, "findMembership" | "findOrgMembersByUsername">; orgDAL: Pick<TOrgDALFactory, "findMembership" | "findOrgMembersByUsername" | "findById">;
projectDAL: Pick<TProjectDALFactory, "findById" | "findProjectGhostUser" | "transaction" | "findProjectById">; projectDAL: Pick<TProjectDALFactory, "findById" | "findProjectGhostUser" | "transaction" | "findProjectById">;
projectKeyDAL: Pick<TProjectKeyDALFactory, "findLatestProjectKey" | "delete" | "insertMany">; projectKeyDAL: Pick<TProjectKeyDALFactory, "findLatestProjectKey" | "delete" | "insertMany">;
licenseService: Pick<TLicenseServiceFactory, "getPlan">; licenseService: Pick<TLicenseServiceFactory, "getPlan">;
projectUserAdditionalPrivilegeDAL: Pick<TProjectUserAdditionalPrivilegeDALFactory, "delete">; additionalPrivilegeDAL: Pick<TAdditionalPrivilegeDALFactory, "delete">;
secretReminderRecipientsDAL: Pick<TSecretReminderRecipientsDALFactory, "delete">; secretReminderRecipientsDAL: Pick<TSecretReminderRecipientsDALFactory, "delete">;
groupProjectDAL: TGroupProjectDALFactory; groupProjectDAL: TGroupProjectDALFactory;
notificationService: Pick<TNotificationServiceFactory, "createUserNotifications">; notificationService: Pick<TNotificationServiceFactory, "createUserNotifications">;
@@ -68,9 +72,7 @@ export const projectMembershipServiceFactory = ({
projectMembershipDAL, projectMembershipDAL,
projectUserMembershipRoleDAL, projectUserMembershipRoleDAL,
smtpService, smtpService,
projectRoleDAL,
orgDAL, orgDAL,
projectUserAdditionalPrivilegeDAL,
userDAL, userDAL,
userGroupMembershipDAL, userGroupMembershipDAL,
groupProjectDAL, groupProjectDAL,
@@ -78,7 +80,11 @@ export const projectMembershipServiceFactory = ({
projectKeyDAL, projectKeyDAL,
secretReminderRecipientsDAL, secretReminderRecipientsDAL,
licenseService, licenseService,
notificationService notificationService,
roleDAL,
membershipRoleDAL,
additionalPrivilegeDAL,
membershipDAL
}: TProjectMembershipServiceFactoryDep) => { }: TProjectMembershipServiceFactoryDep) => {
const getProjectMemberships = async ({ const getProjectMemberships = async ({
actorId, actorId,
@@ -170,6 +176,7 @@ export const projectMembershipServiceFactory = ({
return membership; return membership;
}; };
// TODO(simp): cross-check akhil
const addUsersToProject = async ({ const addUsersToProject = async ({
projectId, projectId,
actorId, actorId,
@@ -191,17 +198,20 @@ export const projectMembershipServiceFactory = ({
actionProjectType: ActionProjectType.Any actionProjectType: ActionProjectType.Any
}); });
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionMemberActions.Create, ProjectPermissionSub.Member); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionMemberActions.Create, ProjectPermissionSub.Member);
const orgMembers = await orgDAL.findMembership({ const orgMembers = await membershipDAL.find({
[`${TableName.OrgMembership}.orgId` as "orgId"]: project.orgId, [`${TableName.Membership}.scopeOrgId` as "scopeOrgId"]: project.orgId,
scope: AccessScope.Organization,
$in: { $in: {
[`${TableName.OrgMembership}.id` as "id"]: members.map(({ orgMembershipId }) => orgMembershipId) [`${TableName.Membership}.id` as "id"]: members.map(({ orgMembershipId }) => orgMembershipId)
} }
}); });
if (orgMembers.length !== members.length) throw new BadRequestError({ message: "Some users are not part of org" }); if (orgMembers.length !== members.length) throw new BadRequestError({ message: "Some users are not part of org" });
const existingMembers = await projectMembershipDAL.find({ const existingMembers = await membershipDAL.find({
projectId, [`${TableName.Membership}.scopeProjectId` as "scopeProjectId"]: projectId,
$in: { userId: orgMembers.map(({ userId }) => userId).filter(Boolean) } scope: AccessScope.Project,
$in: { actorUserId: orgMembers.map(({ actorUserId }) => actorUserId).filter(Boolean) }
}); });
if (existingMembers.length) throw new BadRequestError({ message: "Some users are already part of project" }); if (existingMembers.length) throw new BadRequestError({ message: "Some users are already part of project" });
@@ -212,7 +222,7 @@ export const projectMembershipServiceFactory = ({
) )
); );
await projectMembershipDAL.transaction(async (tx) => { await membershipDAL.transaction(async (tx) => {
const projectMemberships = await projectMembershipDAL.insertMany( const projectMemberships = await projectMembershipDAL.insertMany(
orgMembers.map(({ userId }) => ({ orgMembers.map(({ userId }) => ({
projectId, projectId,
@@ -275,7 +285,7 @@ export const projectMembershipServiceFactory = ({
membershipId, membershipId,
roles roles
}: TUpdateProjectMembershipDTO) => { }: TUpdateProjectMembershipDTO) => {
const { permission, membership } = await permissionService.getProjectPermission({ const { permission, memberships } = await permissionService.getProjectPermission({
actor, actor,
actorId, actorId,
projectId, projectId,
@@ -290,14 +300,14 @@ export const projectMembershipServiceFactory = ({
throw new ForbiddenRequestError({ message: "Forbidden member update" }); throw new ForbiddenRequestError({ message: "Forbidden member update" });
} }
for await (const { role: requestedRoleChange } of roles) { const providedRolePermissionDetails = await permissionService.getProjectPermissionByRoles(
const { permission: rolePermission } = await permissionService.getProjectPermissionByRole( roles.map((el) => el.role).filter((el) => el !== ProjectMembershipRole.NoAccess),
requestedRoleChange, projectId
projectId );
); const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(actorOrgId);
for await (const { permission: rolePermission } of providedRolePermissionDetails) {
const permissionBoundary = validatePrivilegeChangeOperation( const permissionBoundary = validatePrivilegeChangeOperation(
membership.shouldUseNewPrivilegeSystem, shouldUseNewPrivilegeSystem,
ProjectPermissionMemberActions.GrantPrivileges, ProjectPermissionMemberActions.GrantPrivileges,
ProjectPermissionSub.Member, ProjectPermissionSub.Member,
permission, permission,
@@ -306,8 +316,8 @@ export const projectMembershipServiceFactory = ({
if (!permissionBoundary.isValid) if (!permissionBoundary.isValid)
throw new PermissionBoundaryError({ throw new PermissionBoundaryError({
message: constructPermissionErrorMessage( message: constructPermissionErrorMessage(
`Failed to change role ${requestedRoleChange}`, "Failed to assign to role",
membership.shouldUseNewPrivilegeSystem, shouldUseNewPrivilegeSystem,
ProjectPermissionMemberActions.GrantPrivileges, ProjectPermissionMemberActions.GrantPrivileges,
ProjectPermissionSub.Member ProjectPermissionSub.Member
), ),
@@ -322,7 +332,7 @@ export const projectMembershipServiceFactory = ({
// we don't want to include custom in this check; // we don't want to include custom in this check;
// this unintentionally enables setting slug to custom which is reserved // this unintentionally enables setting slug to custom which is reserved
.filter((r) => r !== ProjectMembershipRole.Custom) .filter((r) => r !== ProjectMembershipRole.Custom)
.includes(role as ProjectMembershipRole) .includes(role as ProjectMembershipRole.Member)
); );
const hasCustomRole = Boolean(customInputRoles.length); const hasCustomRole = Boolean(customInputRoles.length);
if (hasCustomRole) { if (hasCustomRole) {
@@ -334,7 +344,7 @@ export const projectMembershipServiceFactory = ({
} }
const customRoles = hasCustomRole const customRoles = hasCustomRole
? await projectRoleDAL.find({ ? await roleDAL.find({
projectId, projectId,
$in: { slug: customInputRoles.map(({ role }) => role) } $in: { slug: customInputRoles.map(({ role }) => role) }
}) })
@@ -368,7 +378,7 @@ export const projectMembershipServiceFactory = ({
}; };
}); });
const updatedRoles = await projectMembershipDAL.transaction(async (tx) => { const updatedRoles = await membershipDAL.transaction(async (tx) => {
await projectUserMembershipRoleDAL.delete({ projectMembershipId: membershipId }, tx); await projectUserMembershipRoleDAL.delete({ projectMembershipId: membershipId }, tx);
return projectUserMembershipRoleDAL.insertMany(sanitizedProjectMembershipRoles, tx); return projectUserMembershipRoleDAL.insertMany(sanitizedProjectMembershipRoles, tx);
}); });
@@ -1,7 +0,0 @@
import { TDbClient } from "@app/db";
import { TableName } from "@app/db/schemas";
import { ormify } from "@app/lib/knex";
export type TProjectRoleDALFactory = ReturnType<typeof projectRoleDALFactory>;
export const projectRoleDALFactory = (db: TDbClient) => ormify(db, TableName.ProjectRoles);
+48 -99
View File
@@ -2,6 +2,7 @@ import { Knex } from "knex";
import { TDbClient } from "@app/db"; import { TDbClient } from "@app/db";
import { import {
AccessScope,
ProjectsSchema, ProjectsSchema,
ProjectType, ProjectType,
ProjectUpgradeStatus, ProjectUpgradeStatus,
@@ -24,10 +25,13 @@ export const projectDALFactory = (db: TDbClient) => {
const findIdentityProjects = async (identityId: string, orgId: string, projectType?: ProjectType) => { const findIdentityProjects = async (identityId: string, orgId: string, projectType?: ProjectType) => {
try { try {
const workspaces = await db(TableName.IdentityProjectMembership) const workspaces = await db
.where({ identityId }) .replicaNode()(TableName.Membership)
.join(TableName.Project, `${TableName.IdentityProjectMembership}.projectId`, `${TableName.Project}.id`) .where(`${TableName.Membership}.scope`, AccessScope.Project)
.where(`${TableName.Membership}.actorIdentityId`, identityId)
.join(TableName.Project, `${TableName.Membership}.scopeProjectId`, `${TableName.Project}.id`)
.where(`${TableName.Project}.orgId`, orgId) .where(`${TableName.Project}.orgId`, orgId)
.join(TableName.Project, `${TableName.Membership}.scopeProjectId`, `${TableName.Project}.id`)
.andWhere((qb) => { .andWhere((qb) => {
if (projectType) { if (projectType) {
void qb.where(`${TableName.Project}.type`, projectType); void qb.where(`${TableName.Project}.type`, projectType);
@@ -74,11 +78,23 @@ export const projectDALFactory = (db: TDbClient) => {
const findUserProjects = async (userId: string, orgId: string, projectType?: ProjectType) => { const findUserProjects = async (userId: string, orgId: string, projectType?: ProjectType) => {
try { try {
const workspaces = await db const userGroupSubquery = db
.replicaNode()(TableName.ProjectMembership) .replicaNode()(TableName.Groups)
.where({ userId }) .leftJoin(TableName.UserGroupMembership, `${TableName.UserGroupMembership}.groupId`, `${TableName.Groups}.id`)
.join(TableName.Project, `${TableName.ProjectMembership}.projectId`, `${TableName.Project}.id`) .where(`${TableName.Groups}.orgId`, orgId)
.where(`${TableName.UserGroupMembership}.userId`, userId)
.select(db.ref("id").withSchema(TableName.Groups));
const projects = await db
.replicaNode()(TableName.Membership)
.where(`${TableName.Membership}.scope`, AccessScope.Project)
.join(TableName.Project, `${TableName.Membership}.scopeProjectId`, `${TableName.Project}.id`)
.where(`${TableName.Project}.orgId`, orgId) .where(`${TableName.Project}.orgId`, orgId)
.andWhere((qb) => {
void qb
.where(`${TableName.Membership}.actorUserId`, userId)
.orWhereIn(`${TableName.Membership}.actorGroupId`, userGroupSubquery);
})
.andWhere((qb) => { .andWhere((qb) => {
if (projectType) { if (projectType) {
void qb.where(`${TableName.Project}.type`, projectType); void qb.where(`${TableName.Project}.type`, projectType);
@@ -97,36 +113,8 @@ export const projectDALFactory = (db: TDbClient) => {
{ column: `${TableName.Environment}.position`, order: "asc" } { column: `${TableName.Environment}.position`, order: "asc" }
]); ]);
const groups = db(TableName.UserGroupMembership).where({ userId }).select("groupId"); const formattedProjects = sqlNestRelationships({
data: projects,
const groupWorkspaces = await db(TableName.GroupProjectMembership)
.whereIn("groupId", groups)
.join(TableName.Project, `${TableName.GroupProjectMembership}.projectId`, `${TableName.Project}.id`)
.where(`${TableName.Project}.orgId`, orgId)
.andWhere((qb) => {
if (projectType) {
void qb.where(`${TableName.Project}.type`, projectType);
}
})
.whereNotIn(
`${TableName.Project}.id`,
workspaces.map(({ id }) => id)
)
.leftJoin(TableName.Environment, `${TableName.Environment}.projectId`, `${TableName.Project}.id`)
.select(
selectAllTableCols(TableName.Project),
db.ref("id").withSchema(TableName.Project).as("_id"),
db.ref("id").withSchema(TableName.Environment).as("envId"),
db.ref("slug").withSchema(TableName.Environment).as("envSlug"),
db.ref("name").withSchema(TableName.Environment).as("envName")
)
.orderBy([
{ column: `${TableName.Project}.name`, order: "asc" },
{ column: `${TableName.Environment}.position`, order: "asc" }
]);
const nestedWorkspaces = sqlNestRelationships({
data: workspaces.concat(groupWorkspaces),
key: "id", key: "id",
parentMapper: ({ _id, ...el }) => ({ _id, ...ProjectsSchema.parse(el) }), parentMapper: ({ _id, ...el }) => ({ _id, ...ProjectsSchema.parse(el) }),
childrenMapper: [ childrenMapper: [
@@ -142,7 +130,7 @@ export const projectDALFactory = (db: TDbClient) => {
] ]
}); });
return nestedWorkspaces.map((workspace) => ({ return formattedProjects.map((workspace) => ({
...workspace, ...workspace,
organization: workspace.orgId organization: workspace.orgId
})); }));
@@ -153,9 +141,10 @@ export const projectDALFactory = (db: TDbClient) => {
const findProjectGhostUser = async (projectId: string, tx?: Knex) => { const findProjectGhostUser = async (projectId: string, tx?: Knex) => {
try { try {
const ghostUser = await (tx || db.replicaNode())(TableName.ProjectMembership) const ghostUser = await (tx || db.replicaNode())(TableName.Membership)
.where({ projectId }) .where(`${TableName.Membership}.scope`, AccessScope.Project)
.join(TableName.Users, `${TableName.ProjectMembership}.userId`, `${TableName.Users}.id`) .where(`${TableName.Membership}.scopeProjectId`, projectId)
.join(TableName.Users, `${TableName.Membership}.actorUserId`, `${TableName.Users}.id`)
.select(selectAllTableCols(TableName.Users)) .select(selectAllTableCols(TableName.Users))
.where({ isGhost: true }) .where({ isGhost: true })
.first(); .first();
@@ -177,54 +166,6 @@ export const projectDALFactory = (db: TDbClient) => {
} }
}; };
const findAllProjectsByIdentity = async (identityId: string, projectType?: ProjectType) => {
try {
const workspaces = await db
.replicaNode()(TableName.IdentityProjectMembership)
.where({ identityId })
.join(TableName.Project, `${TableName.IdentityProjectMembership}.projectId`, `${TableName.Project}.id`)
.andWhere((qb) => {
if (projectType) {
void qb.where(`${TableName.Project}.type`, projectType);
}
})
.leftJoin(TableName.Environment, `${TableName.Environment}.projectId`, `${TableName.Project}.id`)
.select(
selectAllTableCols(TableName.Project),
db.ref("id").withSchema(TableName.Project).as("_id"),
db.ref("id").withSchema(TableName.Environment).as("envId"),
db.ref("slug").withSchema(TableName.Environment).as("envSlug"),
db.ref("name").withSchema(TableName.Environment).as("envName")
)
.orderBy("createdAt", "asc", "last");
const nestedWorkspaces = sqlNestRelationships({
data: workspaces,
key: "id",
parentMapper: ({ _id, ...el }) => ({ _id, ...ProjectsSchema.parse(el) }),
childrenMapper: [
{
key: "envId",
label: "environments" as const,
mapper: ({ envId: id, envSlug: slug, envName: name }) => ({
id,
slug,
name
})
}
]
});
// We need to add the organization field, as it's required for one of our API endpoint responses.
return nestedWorkspaces.map((workspace) => ({
...workspace,
organization: workspace.orgId
}));
} catch (error) {
throw new DatabaseError({ error, name: "Find all projects by identity" });
}
};
const findProjectById = async (id: string) => { const findProjectById = async (id: string) => {
try { try {
const workspaces = await db const workspaces = await db
@@ -402,18 +343,27 @@ export const projectDALFactory = (db: TDbClient) => {
projectIds?: string[]; projectIds?: string[];
}) => { }) => {
const { limit = 20, offset = 0, sortBy = SearchProjectSortBy.NAME, sortDir = SortDirection.ASC } = dto; const { limit = 20, offset = 0, sortBy = SearchProjectSortBy.NAME, sortDir = SortDirection.ASC } = dto;
const groupMembershipSubquery = db(TableName.Groups)
.leftJoin(TableName.UserGroupMembership, `${TableName.UserGroupMembership}.groupId`, `${TableName.Groups}.id`)
.where(`${TableName.Groups}.orgId`, dto.orgId)
.where(`${TableName.UserGroupMembership}.userId`, dto.actorId)
.select(db.ref("id").withSchema(TableName.Groups));
const userMembershipSubquery = db(TableName.Membership)
.where(`${TableName.Membership}.scope`, AccessScope.Project)
.where((qb) => {
if (dto.actor === ActorType.IDENTITY) {
} else {
void qb
.where(`${TableName.Membership}.actorUserId`, dto.actorId)
.orWhereIn(`${TableName.Membership}.actorGroupId`, groupMembershipSubquery);
}
})
.select("scopeProjectId");
const userMembershipSubquery = db(TableName.ProjectMembership).where({ userId: dto.actorId }).select("projectId"); const identityMembershipSubQuery = db(TableName.Membership).where({ identityId: dto.actorId }).select("projectId");
const groups = db(TableName.UserGroupMembership).where({ userId: dto.actorId }).select("groupId");
const groupMembershipSubquery = db(TableName.GroupProjectMembership).whereIn("groupId", groups).select("projectId");
const identityMembershipSubQuery = db(TableName.IdentityProjectMembership)
.where({ identityId: dto.actorId })
.select("projectId");
// Get the SQL strings for the subqueries // Get the SQL strings for the subqueries
const userMembershipSql = userMembershipSubquery.toQuery(); const userMembershipSql = userMembershipSubquery.toQuery();
const groupMembershipSql = groupMembershipSubquery.toQuery();
const identityMembershipSql = identityMembershipSubQuery.toQuery(); const identityMembershipSql = identityMembershipSubQuery.toQuery();
const query = db const query = db
@@ -431,7 +381,7 @@ export const projectDALFactory = (db: TDbClient) => {
ELSE FALSE ELSE FALSE
END as "isMember" END as "isMember"
`, `,
[db.raw(userMembershipSql), db.raw(groupMembershipSql)] [db.raw(userMembershipSql)]
) )
: db.raw( : db.raw(
` `
@@ -495,7 +445,6 @@ export const projectDALFactory = (db: TDbClient) => {
findUserProjects, findUserProjects,
findIdentityProjects, findIdentityProjects,
setProjectUpgradeStatus, setProjectUpgradeStatus,
findAllProjectsByIdentity,
findProjectGhostUser, findProjectGhostUser,
findProjectById, findProjectById,
findProjectByFilter, findProjectByFilter,
@@ -29,6 +29,10 @@ export const newNamespaceRoleFactory = (_dto: TNamespaceRoleScopeFactoryDep): TR
throw new BadRequestError({ message: "Invalid scope provided for the factory" }); throw new BadRequestError({ message: "Invalid scope provided for the factory" });
}; };
const isCustomRole: TRoleScopeFactory["isCustomRole"] = () => false;
const getPredefinedRoles: TRoleScopeFactory["getPredefinedRoles"] = async () => [];
return { return {
onCreateRoleGuard, onCreateRoleGuard,
onUpdateRoleGuard, onUpdateRoleGuard,
@@ -36,6 +40,8 @@ export const newNamespaceRoleFactory = (_dto: TNamespaceRoleScopeFactoryDep): TR
onListRoleGuard, onListRoleGuard,
onGetRoleByIdGuard, onGetRoleByIdGuard,
onGetRoleBySlugGuard, onGetRoleBySlugGuard,
getScopeField getScopeField,
isCustomRole,
getPredefinedRoles
}; };
}; };
@@ -1,11 +1,18 @@
import { ForbiddenError } from "@casl/ability"; import { ForbiddenError } from "@casl/ability";
import { AccessScope } from "@app/db/schemas"; import { AccessScope } from "@app/db/schemas";
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission"; import {
orgAdminPermissions,
orgMemberPermissions,
orgNoAccessPermissions,
OrgPermissionActions,
OrgPermissionSubjects
} from "@app/ee/services/permission/org-permission";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
import { BadRequestError } from "@app/lib/errors"; import { BadRequestError } from "@app/lib/errors";
import { TRoleScopeFactory } from "../role-types"; import { TRoleScopeFactory } from "../role-types";
import { isCustomOrgRole } from "@app/services/org/org-role-fns";
type TOrgRoleScopeFactoryDep = { type TOrgRoleScopeFactoryDep = {
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">; permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
@@ -19,6 +26,8 @@ export const newOrgRoleFactory = ({ permissionService }: TOrgRoleScopeFactoryDep
throw new BadRequestError({ message: "Invalid scope provided for the factory" }); throw new BadRequestError({ message: "Invalid scope provided for the factory" });
}; };
const isCustomRole: TRoleScopeFactory["isCustomRole"] = (role: string) => isCustomOrgRole(role);
const onCreateRoleGuard: TRoleScopeFactory["onCreateRoleGuard"] = async (dto) => { const onCreateRoleGuard: TRoleScopeFactory["onCreateRoleGuard"] = async (dto) => {
const { permission } = await permissionService.getOrgPermission( const { permission } = await permissionService.getOrgPermission(
dto.permission.type, dto.permission.type,
@@ -85,6 +94,42 @@ export const newOrgRoleFactory = ({ permissionService }: TOrgRoleScopeFactoryDep
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Role); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Role);
}; };
const getPredefinedRoles: TRoleScopeFactory["getPredefinedRoles"] = async (scopeData) => {
const scopeField = getScopeField(scopeData);
return [
{
id: "b11b49a9-09a9-4443-916a-4246f9ff2c69", // dummy userid
name: "Admin",
slug: "admin",
orgId: scopeField.value,
description: "Complete administration access over the organization",
permissions: orgAdminPermissions,
createdAt: new Date(),
updatedAt: new Date()
},
{
id: "b11b49a9-09a9-4443-916a-4246f9ff2c70", // dummy user for zod validation in response
name: "Member",
slug: "member",
orgId: scopeField.value,
description: "Non-administrative role in an organization",
permissions: orgMemberPermissions,
createdAt: new Date(),
updatedAt: new Date()
},
{
id: "b10d49a9-09a9-4443-916a-4246f9ff2c72", // dummy user for zod validation in response
name: "No Access",
slug: "no-access",
orgId: scopeField.value,
description: "No access to any resources in the organization",
permissions: orgNoAccessPermissions,
createdAt: new Date(),
updatedAt: new Date()
}
];
};
return { return {
onCreateRoleGuard, onCreateRoleGuard,
onUpdateRoleGuard, onUpdateRoleGuard,
@@ -92,6 +137,8 @@ export const newOrgRoleFactory = ({ permissionService }: TOrgRoleScopeFactoryDep
onListRoleGuard, onListRoleGuard,
onGetRoleByIdGuard, onGetRoleByIdGuard,
onGetRoleBySlugGuard, onGetRoleBySlugGuard,
getScopeField getScopeField,
getPredefinedRoles,
isCustomRole
}; };
}; };
@@ -1,17 +1,35 @@
import { v4 as uuidv4 } from "uuid";
import { ForbiddenError } from "@casl/ability"; import { ForbiddenError } from "@casl/ability";
import { AccessScope, ActionProjectType } from "@app/db/schemas"; import { AccessScope, ActionProjectType, ProjectMembershipRole, ProjectType } from "@app/db/schemas";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import {
isCustomProjectRole,
ProjectPermissionActions,
ProjectPermissionSub
} from "@app/ee/services/permission/project-permission";
import { BadRequestError } from "@app/lib/errors"; import { BadRequestError } from "@app/lib/errors";
import { TRoleScopeFactory } from "../role-types"; import { TRoleScopeFactory } from "../role-types";
import {
cryptographicOperatorPermissions,
projectAdminPermissions,
projectMemberPermissions,
projectNoAccessPermissions,
projectViewerPermission,
sshHostBootstrapPermissions
} from "@app/ee/services/permission/default-roles";
import { TProjectDALFactory } from "@app/services/project/project-dal";
type TProjectRoleScopeFactoryDep = { type TProjectRoleScopeFactoryDep = {
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">; permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
projectDAL: Pick<TProjectDALFactory, "findById">;
}; };
export const newProjectRoleFactory = ({ permissionService }: TProjectRoleScopeFactoryDep): TRoleScopeFactory => { export const newProjectRoleFactory = ({
permissionService,
projectDAL
}: TProjectRoleScopeFactoryDep): TRoleScopeFactory => {
const getScopeField: TRoleScopeFactory["getScopeField"] = (dto) => { const getScopeField: TRoleScopeFactory["getScopeField"] = (dto) => {
if (dto.scope === AccessScope.Project) { if (dto.scope === AccessScope.Project) {
return { key: "projectId" as const, value: dto.projectId }; return { key: "projectId" as const, value: dto.projectId };
@@ -19,6 +37,8 @@ export const newProjectRoleFactory = ({ permissionService }: TProjectRoleScopeFa
throw new BadRequestError({ message: "Invalid scope provided for the factory" }); throw new BadRequestError({ message: "Invalid scope provided for the factory" });
}; };
const isCustomRole: TRoleScopeFactory["isCustomRole"] = (role: string) => isCustomProjectRole(role);
const onCreateRoleGuard: TRoleScopeFactory["onCreateRoleGuard"] = async (dto) => { const onCreateRoleGuard: TRoleScopeFactory["onCreateRoleGuard"] = async (dto) => {
const scope = getScopeField(dto.scopeData); const scope = getScopeField(dto.scopeData);
const { permission } = await permissionService.getProjectPermission({ const { permission } = await permissionService.getProjectPermission({
@@ -97,6 +117,78 @@ export const newProjectRoleFactory = ({ permissionService }: TProjectRoleScopeFa
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Role); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Role);
}; };
const getPredefinedRoles: TRoleScopeFactory["getPredefinedRoles"] = async (scopeData) => {
const scope = getScopeField(scopeData);
const project = await projectDAL.findById(scope.value);
if (!project) throw new BadRequestError({ message: "Project not found" });
const projectId = project.id;
return [
{
id: uuidv4(),
name: "Admin",
slug: ProjectMembershipRole.Admin,
permissions: projectAdminPermissions,
description: "Full administrative access over a project",
createdAt: new Date(),
updatedAt: new Date(),
projectId
},
{
id: uuidv4(),
name: "Developer",
slug: ProjectMembershipRole.Member,
permissions: projectMemberPermissions,
description: "Limited read/write role in a project",
createdAt: new Date(),
updatedAt: new Date(),
projectId
},
{
id: uuidv4(),
name: "SSH Host Bootstrapper",
slug: ProjectMembershipRole.SshHostBootstrapper,
permissions: sshHostBootstrapPermissions,
description: "Create and issue SSH Hosts in a project",
createdAt: new Date(),
updatedAt: new Date(),
projectId,
type: ProjectType.SSH
},
{
id: uuidv4(),
name: "Cryptographic Operator",
slug: ProjectMembershipRole.KmsCryptographicOperator,
permissions: cryptographicOperatorPermissions,
description: "Perform cryptographic operations, such as encryption and signing, in a project",
createdAt: new Date(),
updatedAt: new Date(),
projectId,
type: ProjectType.KMS
},
{
id: uuidv4(),
name: "Viewer",
slug: ProjectMembershipRole.Viewer,
permissions: projectViewerPermission,
description: "Only read role in a project",
createdAt: new Date(),
projectId,
updatedAt: new Date()
},
{
id: uuidv4(),
name: "No Access",
slug: ProjectMembershipRole.NoAccess,
permissions: projectNoAccessPermissions,
description: "No access to any resources in the project",
createdAt: new Date(),
projectId,
updatedAt: new Date()
}
].filter(({ type }) => (type ? type === project.type : true));
};
return { return {
onCreateRoleGuard, onCreateRoleGuard,
onUpdateRoleGuard, onUpdateRoleGuard,
@@ -104,6 +196,8 @@ export const newProjectRoleFactory = ({ permissionService }: TProjectRoleScopeFa
onListRoleGuard, onListRoleGuard,
onGetRoleByIdGuard, onGetRoleByIdGuard,
onGetRoleBySlugGuard, onGetRoleBySlugGuard,
getScopeField getScopeField,
getPredefinedRoles,
isCustomRole
}; };
}; };
+17 -3
View File
@@ -16,20 +16,23 @@ import {
TListRoleDTO, TListRoleDTO,
TUpdateRoleDTO TUpdateRoleDTO
} from "./role-types"; } from "./role-types";
import { TProjectDALFactory } from "../project/project-dal";
type TRoleServiceFactoryDep = { type TRoleServiceFactoryDep = {
roleDAL: TRoleDALFactory; roleDAL: TRoleDALFactory;
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission" | "getOrgPermission">; permissionService: Pick<TPermissionServiceFactory, "getProjectPermission" | "getOrgPermission">;
projectDAL: Pick<TProjectDALFactory, "findById">;
}; };
export type TRoleServiceFactory = ReturnType<typeof roleServiceFactory>; export type TRoleServiceFactory = ReturnType<typeof roleServiceFactory>;
export const roleServiceFactory = ({ roleDAL, permissionService }: TRoleServiceFactoryDep) => { export const roleServiceFactory = ({ roleDAL, permissionService, projectDAL }: TRoleServiceFactoryDep) => {
const orgRoleFactory = newOrgRoleFactory({ const orgRoleFactory = newOrgRoleFactory({
permissionService permissionService
}); });
const projectRoleFactory = newProjectRoleFactory({ const projectRoleFactory = newProjectRoleFactory({
permissionService permissionService,
projectDAL
}); });
const namespaceRoleFactory = newNamespaceRoleFactory({ const namespaceRoleFactory = newNamespaceRoleFactory({
permissionService permissionService
@@ -130,6 +133,7 @@ export const roleServiceFactory = ({ roleDAL, permissionService }: TRoleServiceF
await factory.onListRoleGuard(dto); await factory.onListRoleGuard(dto);
const scope = factory.getScopeField(scopeData); const scope = factory.getScopeField(scopeData);
const predefinedRoles = await factory.getPredefinedRoles(scopeData);
const roles = await roleDAL.find( const roles = await roleDAL.find(
{ {
[scope.key]: scope.value [scope.key]: scope.value
@@ -137,7 +141,9 @@ export const roleServiceFactory = ({ roleDAL, permissionService }: TRoleServiceF
{ limit: dto.data.limit, offset: dto.data.offset, sort: [[`${TableName.Role}.slug` as "slug", "asc"]] } { limit: dto.data.limit, offset: dto.data.offset, sort: [[`${TableName.Role}.slug` as "slug", "asc"]] }
); );
return { roles: roles.map((el) => ({ ...el, permissions: unpackPermissions(el.permissions) })) }; return {
roles: [...predefinedRoles, ...roles.map((el) => ({ ...el, permissions: unpackPermissions(el.permissions) }))]
};
}; };
const getRoleById = async (dto: TGetRoleByIdDTO) => { const getRoleById = async (dto: TGetRoleByIdDTO) => {
@@ -163,6 +169,14 @@ export const roleServiceFactory = ({ roleDAL, permissionService }: TRoleServiceF
await factory.onGetRoleBySlugGuard(dto); await factory.onGetRoleBySlugGuard(dto);
const scope = factory.getScopeField(scopeData); const scope = factory.getScopeField(scopeData);
const isCustomRole = factory.isCustomRole(dto.selector.slug);
if (!isCustomRole) {
const predefinedRole = await factory.getPredefinedRoles(scopeData);
const selectedRole = predefinedRole.find((el) => el.slug === dto.selector.slug);
if (!selectedRole) throw new BadRequestError({ message: `Role with slug ${dto.selector.slug} not found` });
return selectedRole;
}
const role = await roleDAL.findOne({ const role = await roleDAL.findOne({
slug: selector.slug, slug: selector.slug,
[scope.key]: scope.value [scope.key]: scope.value
+4 -1
View File
@@ -1,14 +1,17 @@
import { AccessScopeData } from "@app/db/schemas"; import { AccessScopeData, TRoles } from "@app/db/schemas";
import { OrgServiceActor } from "@app/lib/types"; import { OrgServiceActor } from "@app/lib/types";
import { MongoAbility, RawRuleOf } from "@casl/ability";
export interface TRoleScopeFactory { export interface TRoleScopeFactory {
onCreateRoleGuard: (arg: TCreateRoleDTO) => Promise<void>; onCreateRoleGuard: (arg: TCreateRoleDTO) => Promise<void>;
onUpdateRoleGuard: (arg: TUpdateRoleDTO) => Promise<void>; onUpdateRoleGuard: (arg: TUpdateRoleDTO) => Promise<void>;
onDeleteRoleGuard: (arg: TDeleteRoleDTO) => Promise<void>; onDeleteRoleGuard: (arg: TDeleteRoleDTO) => Promise<void>;
onListRoleGuard: (arg: TListRoleDTO) => Promise<void>; onListRoleGuard: (arg: TListRoleDTO) => Promise<void>;
getPredefinedRoles: (arg: AccessScopeData) => Promise<(TRoles & { permissions: RawRuleOf<MongoAbility>[] })[]>;
onGetRoleByIdGuard: (arg: TGetRoleByIdDTO) => Promise<void>; onGetRoleByIdGuard: (arg: TGetRoleByIdDTO) => Promise<void>;
onGetRoleBySlugGuard: (arg: TGetRoleBySlugDTO) => Promise<void>; onGetRoleBySlugGuard: (arg: TGetRoleBySlugDTO) => Promise<void>;
getScopeField: (scope: AccessScopeData) => { key: "orgId" | "namespaceId" | "projectId"; value: string }; getScopeField: (scope: AccessScopeData) => { key: "orgId" | "namespaceId" | "projectId"; value: string };
isCustomRole: (role: string) => boolean;
} }
export type TCreateRoleDTO = { export type TCreateRoleDTO = {
@@ -1,7 +1,7 @@
import { Knex } from "knex"; import { Knex } from "knex";
import { TDbClient } from "@app/db"; import { TDbClient } from "@app/db";
import { TableName } from "@app/db/schemas"; import { AccessScope, TableName } from "@app/db/schemas";
import { ormify, selectAllTableCols } from "@app/lib/knex"; import { ormify, selectAllTableCols } from "@app/lib/knex";
export type TSecretReminderRecipientsDALFactory = ReturnType<typeof secretReminderRecipientsDALFactory>; export type TSecretReminderRecipientsDALFactory = ReturnType<typeof secretReminderRecipientsDALFactory>;
@@ -14,13 +14,13 @@ export const secretReminderRecipientsDALFactory = (db: TDbClient) => {
.where({ secretId }) .where({ secretId })
.leftJoin(TableName.Users, `${TableName.SecretReminderRecipients}.userId`, `${TableName.Users}.id`) .leftJoin(TableName.Users, `${TableName.SecretReminderRecipients}.userId`, `${TableName.Users}.id`)
.leftJoin(TableName.Project, `${TableName.SecretReminderRecipients}.projectId`, `${TableName.Project}.id`) .leftJoin(TableName.Project, `${TableName.SecretReminderRecipients}.projectId`, `${TableName.Project}.id`)
.leftJoin(TableName.OrgMembership, (bd) => { .leftJoin(TableName.Membership, (bd) => {
void bd void bd
.on(`${TableName.OrgMembership}.userId`, "=", `${TableName.SecretReminderRecipients}.userId`) .on(`${TableName.Membership}.actorUserId`, "=", `${TableName.SecretReminderRecipients}.userId`)
.andOn(`${TableName.OrgMembership}.orgId`, "=", `${TableName.Project}.orgId`); .andOn(`${TableName.Membership}.scopeOrgId`, "=", `${TableName.Project}.orgId`)
.andOn(`${TableName.Membership}.scope`, db.raw("?", [AccessScope.Organization]));
}) })
.where(`${TableName.Membership}.isActive`, true)
.where(`${TableName.OrgMembership}.isActive`, true)
.select(selectAllTableCols(TableName.SecretReminderRecipients)) .select(selectAllTableCols(TableName.SecretReminderRecipients))
.select( .select(
db.ref("email").withSchema(TableName.Users).as("email"), db.ref("email").withSchema(TableName.Users).as("email"),
@@ -2,7 +2,13 @@
import { Knex } from "knex"; import { Knex } from "knex";
import { TDbClient } from "@app/db"; import { TDbClient } from "@app/db";
import { SecretVersionsV2Schema, TableName, TSecretVersionsV2, TSecretVersionsV2Update } from "@app/db/schemas"; import {
AccessScope,
SecretVersionsV2Schema,
TableName,
TSecretVersionsV2,
TSecretVersionsV2Update
} from "@app/db/schemas";
import { BadRequestError, DatabaseError } from "@app/lib/errors"; import { BadRequestError, DatabaseError } from "@app/lib/errors";
import { buildFindFilter, ormify, selectAllTableCols, sqlNestRelationships, TFindOpt } from "@app/lib/knex"; import { buildFindFilter, ormify, selectAllTableCols, sqlNestRelationships, TFindOpt } from "@app/lib/knex";
import { logger } from "@app/lib/logger"; import { logger } from "@app/lib/logger";
@@ -191,11 +197,11 @@ export const secretVersionV2BridgeDALFactory = (db: TDbClient) => {
const { offset, limit, sort = [["createdAt", "desc"]] } = findOpt; const { offset, limit, sort = [["createdAt", "desc"]] } = findOpt;
const query = (tx || db.replicaNode())(TableName.SecretVersionV2) const query = (tx || db.replicaNode())(TableName.SecretVersionV2)
.leftJoin(TableName.Users, `${TableName.Users}.id`, `${TableName.SecretVersionV2}.userActorId`) .leftJoin(TableName.Users, `${TableName.Users}.id`, `${TableName.SecretVersionV2}.userActorId`)
.leftJoin( .leftJoin(TableName.Membership, (qb) => {
TableName.ProjectMembership, void qb
`${TableName.ProjectMembership}.userId`, .on(`${TableName.Membership}.actorUserId`, `${TableName.SecretVersionV2}.userActorId`)
`${TableName.SecretVersionV2}.userActorId` .andOn(`${TableName.Membership}.scope`, db.raw("?", [AccessScope.Project]));
) })
.leftJoin(TableName.Identity, `${TableName.Identity}.id`, `${TableName.SecretVersionV2}.identityActorId`) .leftJoin(TableName.Identity, `${TableName.Identity}.id`, `${TableName.SecretVersionV2}.identityActorId`)
.leftJoin(TableName.SecretV2, `${TableName.SecretVersionV2}.secretId`, `${TableName.SecretV2}.id`) .leftJoin(TableName.SecretV2, `${TableName.SecretVersionV2}.secretId`, `${TableName.SecretV2}.id`)
.leftJoin( .leftJoin(
@@ -210,19 +216,19 @@ export const secretVersionV2BridgeDALFactory = (db: TDbClient) => {
) )
.where((qb) => { .where((qb) => {
void qb.where(`${TableName.SecretVersionV2}.secretId`, secretId); void qb.where(`${TableName.SecretVersionV2}.secretId`, secretId);
void qb.where(`${TableName.ProjectMembership}.projectId`, projectId); void qb.where(`${TableName.Membership}.scopeProjectId`, projectId);
if (secretVersions?.length) void qb.whereIn(`${TableName.SecretVersionV2}.version`, secretVersions); if (secretVersions?.length) void qb.whereIn(`${TableName.SecretVersionV2}.version`, secretVersions);
}) })
.orWhere((qb) => { .orWhere((qb) => {
void qb.where(`${TableName.SecretVersionV2}.secretId`, secretId); void qb.where(`${TableName.SecretVersionV2}.secretId`, secretId);
void qb.whereNull(`${TableName.ProjectMembership}.projectId`); void qb.whereNull(`${TableName.Membership}.actorProjectId`);
if (secretVersions?.length) void qb.whereIn(`${TableName.SecretVersionV2}.version`, secretVersions); if (secretVersions?.length) void qb.whereIn(`${TableName.SecretVersionV2}.version`, secretVersions);
}) })
.select( .select(
selectAllTableCols(TableName.SecretVersionV2), selectAllTableCols(TableName.SecretVersionV2),
db.ref("username").withSchema(TableName.Users).as("userActorName"), db.ref("username").withSchema(TableName.Users).as("userActorName"),
db.ref("name").withSchema(TableName.Identity).as("identityActorName"), db.ref("name").withSchema(TableName.Identity).as("identityActorName"),
db.ref("id").withSchema(TableName.ProjectMembership).as("membershipId"), db.ref("id").withSchema(TableName.Membership).as("membershipId"),
db.ref("id").withSchema(TableName.SecretTag).as("tagId"), db.ref("id").withSchema(TableName.SecretTag).as("tagId"),
db.ref("color").withSchema(TableName.SecretTag).as("tagColor"), db.ref("color").withSchema(TableName.SecretTag).as("tagColor"),
db.ref("slug").withSchema(TableName.SecretTag).as("tagSlug") db.ref("slug").withSchema(TableName.SecretTag).as("tagSlug")
+17 -8
View File
@@ -2,6 +2,7 @@ import { Knex } from "knex";
import { TDbClient } from "@app/db"; import { TDbClient } from "@app/db";
import { import {
AccessScope,
TableName, TableName,
TUserActionsInsert, TUserActionsInsert,
TUserActionsUpdate, TUserActionsUpdate,
@@ -127,9 +128,13 @@ export const userDALFactory = (db: TDbClient) => {
const findUserByProjectMembershipId = async (projectMembershipId: string) => { const findUserByProjectMembershipId = async (projectMembershipId: string) => {
try { try {
return await db return await db
.replicaNode()(TableName.ProjectMembership) .replicaNode()(TableName.Membership)
.where({ [`${TableName.ProjectMembership}.id` as "id"]: projectMembershipId }) .where({
.join(TableName.Users, `${TableName.ProjectMembership}.userId`, `${TableName.Users}.id`) [`${TableName.Membership}.id` as "id"]: projectMembershipId,
[`${TableName.Membership}.scope` as "scope"]: AccessScope.Project
})
.whereNotNull(`${TableName.Membership}.actorUserId`)
.join(TableName.Users, `${TableName.Membership}.actorUserId`, `${TableName.Users}.id`)
.first(); .first();
} catch (error) { } catch (error) {
throw new DatabaseError({ error, name: "Find user by project membership id" }); throw new DatabaseError({ error, name: "Find user by project membership id" });
@@ -139,9 +144,11 @@ export const userDALFactory = (db: TDbClient) => {
const findUsersByProjectMembershipIds = async (projectMembershipIds: string[]) => { const findUsersByProjectMembershipIds = async (projectMembershipIds: string[]) => {
try { try {
return await db return await db
.replicaNode()(TableName.ProjectMembership) .replicaNode()(TableName.Membership)
.whereIn(`${TableName.ProjectMembership}.id`, projectMembershipIds) .whereIn(`${TableName.Membership}.id`, projectMembershipIds)
.join(TableName.Users, `${TableName.ProjectMembership}.userId`, `${TableName.Users}.id`) .where(`${TableName.Membership}.scope`, AccessScope.Project)
.whereNotNull(`${TableName.Membership}.actorUserId`)
.join(TableName.Users, `${TableName.Membership}.actorUserId`, `${TableName.Users}.id`)
.select("*"); .select("*");
} catch (error) { } catch (error) {
throw new DatabaseError({ error, name: "Find users by project membership ids" }); throw new DatabaseError({ error, name: "Find users by project membership ids" });
@@ -191,8 +198,10 @@ export const userDALFactory = (db: TDbClient) => {
try { try {
const doc = await db(TableName.Users) const doc = await db(TableName.Users)
.where({ email }) .where({ email })
.leftJoin(TableName.OrgMembership, `${TableName.OrgMembership}.userId`, `${TableName.Users}.id`) .leftJoin(TableName.Membership, `${TableName.Membership}.actorUserId`, `${TableName.Users}.id`)
.leftJoin(TableName.Organization, `${TableName.Organization}.id`, `${TableName.OrgMembership}.orgId`) .where(`${TableName.Membership}.scope`, AccessScope.Organization)
.whereNotNull(`${TableName.Membership}.actorUserId`)
.leftJoin(TableName.Organization, `${TableName.Organization}.id`, `${TableName.Membership}.scopeOrgId`)
.select(selectAllTableCols(TableName.Users)) .select(selectAllTableCols(TableName.Users))
.select( .select(
db.ref("name").withSchema(TableName.Organization).as("orgName"), db.ref("name").withSchema(TableName.Organization).as("orgName"),