mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
feat: switched up all dal except policy ones
This commit is contained in:
@@ -36,7 +36,7 @@ export async function up(knex: Knex): Promise<void> {
|
||||
t.uuid("scopeNamespaceId");
|
||||
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("inviteEmail");
|
||||
t.datetime("lastInvitedAt");
|
||||
|
||||
@@ -261,7 +261,16 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
memberships: OrgMembershipsSchema.array(),
|
||||
memberships: z
|
||||
.object({
|
||||
id: z.string(),
|
||||
roles: z
|
||||
.object({
|
||||
role: z.string()
|
||||
})
|
||||
.array()
|
||||
})
|
||||
.array(),
|
||||
permissions: z.any().array()
|
||||
})
|
||||
}
|
||||
@@ -274,7 +283,16 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
|
||||
req.permission.authMethod,
|
||||
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 { 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 { checkForInvalidPermissionCombination } from "@app/ee/services/permission/permission-fns";
|
||||
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 => {
|
||||
const accessApprovalRequestOrm = ormify(db, TableName.AccessApprovalRequest);
|
||||
|
||||
// TODO(simp): check this from begginging
|
||||
const findRequestsWithPrivilegeByPolicyIds: TAccessApprovalRequestDALFactory["findRequestsWithPrivilegeByPolicyIds"] =
|
||||
async (policyIds) => {
|
||||
try {
|
||||
const docs = await db
|
||||
.replicaNode()(TableName.AccessApprovalRequest)
|
||||
.whereIn(`${TableName.AccessApprovalRequest}.policyId`, policyIds)
|
||||
|
||||
.leftJoin(
|
||||
TableName.ProjectUserAdditionalPrivilege,
|
||||
TableName.AdditionalPrivilege,
|
||||
`${TableName.AccessApprovalRequest}.privilegeId`,
|
||||
`${TableName.ProjectUserAdditionalPrivilege}.id`
|
||||
`${TableName.AdditionalPrivilege}.id`
|
||||
)
|
||||
.leftJoin(
|
||||
TableName.AccessApprovalPolicy,
|
||||
@@ -276,7 +276,6 @@ export const accessApprovalRequestDALFactory = (db: TDbClient): TAccessApprovalR
|
||||
`${TableName.UserGroupMembership}.groupId`
|
||||
)
|
||||
.leftJoin(TableName.Users, `${TableName.UserGroupMembership}.userId`, `${TableName.Users}.id`)
|
||||
|
||||
.leftJoin(
|
||||
TableName.AccessApprovalPolicyBypasser,
|
||||
`${TableName.AccessApprovalPolicy}.id`,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
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 { buildFindFilter, ormify, selectAllTableCols, TFindFilter, TFindOpt } from "@app/lib/knex";
|
||||
|
||||
@@ -81,9 +81,11 @@ export const groupDALFactory = (db: TDbClient) => {
|
||||
}) => {
|
||||
try {
|
||||
const query = db
|
||||
.replicaNode()(TableName.OrgMembership)
|
||||
.where(`${TableName.OrgMembership}.orgId`, orgId)
|
||||
.join(TableName.Users, `${TableName.OrgMembership}.userId`, `${TableName.Users}.id`)
|
||||
.replicaNode()(TableName.Membership)
|
||||
.where(`${TableName.Membership}.scopeOrgId`, orgId)
|
||||
.where(`${TableName.Membership}.scope`, AccessScope.Organization)
|
||||
.whereNotNull(`${TableName.Membership}.actorUserId`)
|
||||
.join(TableName.Users, `${TableName.Membership}.actorUserId`, `${TableName.Users}.id`)
|
||||
.leftJoin(TableName.UserGroupMembership, (bd) => {
|
||||
bd.on(`${TableName.UserGroupMembership}.userId`, "=", `${TableName.Users}.id`).andOn(
|
||||
`${TableName.UserGroupMembership}.groupId`,
|
||||
@@ -92,7 +94,7 @@ export const groupDALFactory = (db: TDbClient) => {
|
||||
);
|
||||
})
|
||||
.select(
|
||||
db.ref("id").withSchema(TableName.OrgMembership),
|
||||
db.ref("id").withSchema(TableName.Membership),
|
||||
db.ref("groupId").withSchema(TableName.UserGroupMembership),
|
||||
db.ref("createdAt").withSchema(TableName.UserGroupMembership).as("joinedGroupAt"),
|
||||
db.ref("email").withSchema(TableName.Users),
|
||||
@@ -160,8 +162,10 @@ export const groupDALFactory = (db: TDbClient) => {
|
||||
const findGroupsByProjectId = async (projectId: string, tx?: Knex) => {
|
||||
try {
|
||||
const docs = await (tx || db.replicaNode())(TableName.Groups)
|
||||
.join(TableName.GroupProjectMembership, `${TableName.Groups}.id`, `${TableName.GroupProjectMembership}.groupId`)
|
||||
.where(`${TableName.GroupProjectMembership}.projectId`, projectId)
|
||||
.join(TableName.Membership, `${TableName.Membership}.actorGroupId`, `${TableName.Groups}.id`)
|
||||
.where(`${TableName.Membership}.scopeProjectId`, projectId)
|
||||
.where(`${TableName.Membership}.scope`, AccessScope.Project)
|
||||
.whereNotNull(`${TableName.Membership}.actorGroupId`)
|
||||
.select(selectAllTableCols(TableName.Groups));
|
||||
return docs;
|
||||
} catch (error) {
|
||||
@@ -172,11 +176,16 @@ export const groupDALFactory = (db: TDbClient) => {
|
||||
const findById = async (id: string, tx?: Knex) => {
|
||||
try {
|
||||
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.Membership}.scope`, AccessScope.Organization)
|
||||
.select(
|
||||
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();
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
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 { 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) => {
|
||||
try {
|
||||
const userProjectMemberships: string[] = await (tx || db.replicaNode())(TableName.ProjectMembership)
|
||||
.where(`${TableName.ProjectMembership}.userId`, userId)
|
||||
.whereIn(`${TableName.ProjectMembership}.projectId`, projectIds)
|
||||
.pluck(`${TableName.ProjectMembership}.projectId`);
|
||||
const userProjectMemberships: string[] = await (tx || db.replicaNode())(TableName.Membership)
|
||||
.where(`${TableName.Membership}.actorUserId`, userId)
|
||||
.where(`${TableName.Membership}.scope`, AccessScope.Project)
|
||||
.whereIn(`${TableName.Membership}.scopeProjectId`, projectIds)
|
||||
.pluck(`${TableName.Membership}.scopeProjectId`);
|
||||
|
||||
const userGroupMemberships: string[] = await (tx || db.replicaNode())(TableName.UserGroupMembership)
|
||||
.where(`${TableName.UserGroupMembership}.userId`, userId)
|
||||
.whereNot(`${TableName.UserGroupMembership}.groupId`, groupId)
|
||||
.join(
|
||||
TableName.GroupProjectMembership,
|
||||
`${TableName.UserGroupMembership}.groupId`,
|
||||
`${TableName.GroupProjectMembership}.groupId`
|
||||
)
|
||||
.whereIn(`${TableName.GroupProjectMembership}.projectId`, projectIds)
|
||||
.pluck(`${TableName.GroupProjectMembership}.projectId`);
|
||||
.join(TableName.Membership, `${TableName.UserGroupMembership}.groupId`, `${TableName.Membership}.actorGroupId`)
|
||||
.where(`${TableName.Membership}.scope`, AccessScope.Project)
|
||||
.whereIn(`${TableName.Membership}.scopeProjectId`, projectIds)
|
||||
.pluck(`${TableName.Membership}.scopeProjectId`);
|
||||
|
||||
return new Set(userProjectMemberships.concat(userGroupMemberships));
|
||||
} catch (error) {
|
||||
@@ -44,13 +42,10 @@ export const userGroupMembershipDALFactory = (db: TDbClient) => {
|
||||
const findUserGroupMembershipsInProject = async (usernames: string[], projectId: string, tx?: Knex) => {
|
||||
try {
|
||||
const usernameDocs: string[] = await (tx || db.replicaNode())(TableName.UserGroupMembership)
|
||||
.join(
|
||||
TableName.GroupProjectMembership,
|
||||
`${TableName.UserGroupMembership}.groupId`,
|
||||
`${TableName.GroupProjectMembership}.groupId`
|
||||
)
|
||||
.join(TableName.Membership, `${TableName.UserGroupMembership}.groupId`, `${TableName.Membership}.actorGroupId`)
|
||||
.where(`${TableName.Membership}.scope`, AccessScope.Project)
|
||||
.join(TableName.Users, `${TableName.UserGroupMembership}.userId`, `${TableName.Users}.id`)
|
||||
.where(`${TableName.GroupProjectMembership}.projectId`, projectId)
|
||||
.where(`${TableName.Membership}.scopeProjectId`, projectId)
|
||||
.whereIn(`${TableName.Users}.username`, usernames)
|
||||
.pluck(`${TableName.Users}.id`);
|
||||
|
||||
@@ -73,24 +68,25 @@ export const userGroupMembershipDALFactory = (db: TDbClient) => {
|
||||
try {
|
||||
// get list of groups in the project with id [projectId]
|
||||
// that that are not the group with id [groupId]
|
||||
const groups: string[] = await (tx || db.replicaNode())(TableName.GroupProjectMembership)
|
||||
.where(`${TableName.GroupProjectMembership}.projectId`, projectId)
|
||||
.whereNot(`${TableName.GroupProjectMembership}.groupId`, groupId)
|
||||
.pluck(`${TableName.GroupProjectMembership}.groupId`);
|
||||
const groups: string[] = await (tx || db.replicaNode())(TableName.Membership)
|
||||
.where(`${TableName.Membership}.scopeProjectId`, projectId)
|
||||
.whereNot(`${TableName.Membership}.actorGroupId`, groupId)
|
||||
.pluck(`${TableName.Membership}.groupId`);
|
||||
|
||||
// main query
|
||||
const members = await (tx || db.replicaNode())(TableName.UserGroupMembership)
|
||||
.where(`${TableName.UserGroupMembership}.groupId`, groupId)
|
||||
.where(`${TableName.UserGroupMembership}.isPending`, false)
|
||||
.join(TableName.Users, `${TableName.UserGroupMembership}.userId`, `${TableName.Users}.id`)
|
||||
.leftJoin(TableName.ProjectMembership, (bd) => {
|
||||
bd.on(`${TableName.Users}.id`, "=", `${TableName.ProjectMembership}.userId`).andOn(
|
||||
`${TableName.ProjectMembership}.projectId`,
|
||||
.leftJoin(TableName.Membership, (bd) => {
|
||||
bd.on(`${TableName.Users}.id`, "=", `${TableName.Membership}.actorUserId`).andOn(
|
||||
`${TableName.Membership}.scopeProjectId`,
|
||||
"=",
|
||||
db.raw("?", [projectId])
|
||||
);
|
||||
})
|
||||
.whereNull(`${TableName.ProjectMembership}.userId`)
|
||||
.whereNull(`${TableName.Membership}.actorUserId`)
|
||||
.where(`${TableName.Membership}.scope`, AccessScope.Project)
|
||||
.leftJoin<TUserEncryptionKeys>(
|
||||
TableName.UserEncryptionKey,
|
||||
`${TableName.UserEncryptionKey}.userId`,
|
||||
@@ -166,15 +162,17 @@ export const userGroupMembershipDALFactory = (db: TDbClient) => {
|
||||
const docs = await db
|
||||
.replicaNode()(TableName.UserGroupMembership)
|
||||
.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`)
|
||||
.where(`${TableName.UserGroupMembership}.userId`, userId)
|
||||
.where(`${TableName.Membership}.scope`, AccessScope.Organization)
|
||||
.where(`${TableName.Membership}.scopeOrgId`, orgId)
|
||||
.where(`${TableName.Groups}.orgId`, orgId)
|
||||
.select(
|
||||
db.ref("id").withSchema(TableName.UserGroupMembership),
|
||||
db.ref("groupId").withSchema(TableName.UserGroupMembership),
|
||||
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("lastName").withSchema(TableName.Users).as("lastName"),
|
||||
db.ref("slug").withSchema(TableName.Groups).as("groupSlug")
|
||||
@@ -191,15 +189,17 @@ export const userGroupMembershipDALFactory = (db: TDbClient) => {
|
||||
const docs = await db
|
||||
.replicaNode()(TableName.UserGroupMembership)
|
||||
.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`)
|
||||
.where(`${TableName.Groups}.id`, groupId)
|
||||
.where(`${TableName.Membership}.scope`, AccessScope.Organization)
|
||||
.where(`${TableName.Membership}.scopeOrgId`, orgId)
|
||||
.where(`${TableName.Groups}.orgId`, orgId)
|
||||
.select(
|
||||
db.ref("id").withSchema(TableName.UserGroupMembership),
|
||||
db.ref("groupId").withSchema(TableName.UserGroupMembership),
|
||||
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("lastName").withSchema(TableName.Users).as("lastName")
|
||||
);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
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";
|
||||
|
||||
export type TLicenseDALFactory = ReturnType<typeof licenseDALFactory>;
|
||||
@@ -9,14 +9,14 @@ export type TLicenseDALFactory = ReturnType<typeof licenseDALFactory>;
|
||||
export const licenseDALFactory = (db: TDbClient) => {
|
||||
const countOfOrgMembers = async (orgId: string | null, tx?: Knex) => {
|
||||
try {
|
||||
const doc = await (tx || db.replicaNode())(TableName.OrgMembership)
|
||||
.where({ status: OrgMembershipStatus.Accepted })
|
||||
const doc = await (tx || db.replicaNode())(TableName.Membership)
|
||||
.where({ status: OrgMembershipStatus.Accepted, scope: AccessScope.Organization })
|
||||
.andWhere((bd) => {
|
||||
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)
|
||||
.count();
|
||||
return Number(doc?.[0]?.count ?? 0);
|
||||
@@ -28,24 +28,27 @@ export const licenseDALFactory = (db: TDbClient) => {
|
||||
const countOrgUsersAndIdentities = async (orgId: string | null, tx?: Knex) => {
|
||||
try {
|
||||
// count org users
|
||||
const userDoc = await (tx || db.replicaNode())(TableName.OrgMembership)
|
||||
.where({ status: OrgMembershipStatus.Accepted })
|
||||
const userDoc = await (tx || db.replicaNode())(TableName.Membership)
|
||||
.where({ status: OrgMembershipStatus.Accepted, scope: AccessScope.Organization })
|
||||
.whereNotNull(`${TableName.Membership}.actorUserId`)
|
||||
.andWhere((bd) => {
|
||||
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)
|
||||
.count();
|
||||
|
||||
const userCount = Number(userDoc?.[0].count);
|
||||
|
||||
// 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) => {
|
||||
if (orgId) {
|
||||
void bd.where({ orgId });
|
||||
void bd.where(`${TableName.Membership}.scopeOrgId`, orgId);
|
||||
}
|
||||
})
|
||||
.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;
|
||||
};
|
||||
@@ -252,7 +252,6 @@ import { offlineUsageReportServiceFactory } from "@app/services/offline-usage-re
|
||||
import { incidentContactDALFactory } from "@app/services/org/incident-contacts-dal";
|
||||
import { orgBotDALFactory } from "@app/services/org/org-bot-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 { orgServiceFactory } from "@app/services/org/org-service";
|
||||
import { orgAdminServiceFactory } from "@app/services/org-admin/org-admin-service";
|
||||
@@ -394,7 +393,6 @@ export const registerRoutes = async (
|
||||
const orgMembershipDAL = orgMembershipDALFactory(db);
|
||||
const orgBotDAL = orgBotDALFactory(db);
|
||||
const incidentContactDAL = incidentContactDALFactory(db);
|
||||
const orgRoleDAL = orgRoleDALFactory(db);
|
||||
const rateLimitDAL = rateLimitDALFactory(db);
|
||||
const apiKeyDAL = apiKeyDALFactory(db);
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import {
|
||||
AccessScope,
|
||||
OrgMembershipsSchema,
|
||||
ProjectMembershipsSchema,
|
||||
ProjectUserMembershipRolesSchema,
|
||||
@@ -66,14 +67,23 @@ export const registerDeprecatedProjectMembershipRouter = async (server: FastifyZ
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const memberships = await server.services.projectMembership.getProjectMemberships({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
projectId: req.params.workspaceId
|
||||
const { data: memberships } = await server.services.membershipUser.listMemberships({
|
||||
permission: req.permission,
|
||||
scopeData: {
|
||||
scope: AccessScope.Project,
|
||||
orgId: req.permission.orgId,
|
||||
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]),
|
||||
handler: async (req) => {
|
||||
const membership = await server.services.projectMembership.getProjectMembershipById({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
projectId: req.params.workspaceId,
|
||||
id: req.params.membershipId
|
||||
const { userId } = await server.services.convertor.userMembershipIdToUserId(
|
||||
req.params.membershipId,
|
||||
AccessScope.Project,
|
||||
req.permission.orgId
|
||||
);
|
||||
const membership = await server.services.membershipUser.getMembershipByUserId({
|
||||
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,
|
||||
event: {
|
||||
type: EventType.ADD_BATCH_PROJECT_MEMBER,
|
||||
metadata: data.map(({ userId }) => ({
|
||||
userId: userId || "",
|
||||
metadata: data.map(({ actorUserId }) => ({
|
||||
userId: actorUserId || "",
|
||||
email: ""
|
||||
}))
|
||||
}
|
||||
@@ -300,30 +325,28 @@ export const registerDeprecatedProjectMembershipRouter = async (server: FastifyZ
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const roles = await server.services.projectMembership.updateProjectMembership({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
projectId: req.params.workspaceId,
|
||||
membershipId: req.params.membershipId,
|
||||
roles: req.body.roles
|
||||
const { userId } = await server.services.convertor.userMembershipIdToUserId(
|
||||
req.params.membershipId,
|
||||
AccessScope.Project,
|
||||
req.permission.orgId
|
||||
);
|
||||
|
||||
const { membership } = await server.services.membershipUser.updateMembership({
|
||||
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({
|
||||
// ...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 };
|
||||
return { roles: membership.roles.map((el) => ({ ...el, projectMembershipId: req.params.membershipId })) };
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import {
|
||||
AccessScope,
|
||||
IdentitiesSchema,
|
||||
IdentityProjectMembershipsSchema,
|
||||
ProjectMembershipRole,
|
||||
ProjectUserMembershipRolesSchema
|
||||
ProjectUserMembershipRolesSchema,
|
||||
TemporaryPermissionMode
|
||||
} from "@app/db/schemas";
|
||||
import { ApiDocsTags, ORGANIZATIONS, PROJECT_IDENTITIES } from "@app/lib/api-docs";
|
||||
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 { AuthMode } from "@app/services/auth/auth-type";
|
||||
import { ProjectIdentityOrderBy } from "@app/services/identity-project/identity-project-types";
|
||||
import { ProjectUserMembershipTemporaryMode } from "@app/services/project-membership/project-membership-types";
|
||||
|
||||
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),
|
||||
isTemporary: z.literal(true).describe(PROJECT_IDENTITIES.CREATE_IDENTITY_MEMBERSHIP.roles.role),
|
||||
temporaryMode: z
|
||||
.nativeEnum(ProjectUserMembershipTemporaryMode)
|
||||
.nativeEnum(TemporaryPermissionMode)
|
||||
.describe(PROJECT_IDENTITIES.CREATE_IDENTITY_MEMBERSHIP.roles.role),
|
||||
temporaryRange: z
|
||||
.string()
|
||||
@@ -82,16 +83,22 @@ export const registerIdentityProjectRouter = async (server: FastifyZodProvider)
|
||||
const { role, roles } = req.body;
|
||||
if (!role && !roles) throw new BadRequestError({ message: "You must provide either role or roles field" });
|
||||
|
||||
const identityMembership = await server.services.identityProject.createProjectIdentity({
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
identityId: req.params.identityId,
|
||||
projectId: req.params.projectId,
|
||||
roles: roles || [{ role }]
|
||||
const { membership } = await server.services.membershipIdentity.createMembership({
|
||||
permission: req.permission,
|
||||
scopeData: {
|
||||
scope: AccessScope.Project,
|
||||
orgId: req.permission.orgId,
|
||||
projectId: req.params.projectId
|
||||
},
|
||||
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),
|
||||
isTemporary: z.literal(true).describe(PROJECT_IDENTITIES.UPDATE_IDENTITY_MEMBERSHIP.roles.isTemporary),
|
||||
temporaryMode: z
|
||||
.nativeEnum(ProjectUserMembershipTemporaryMode)
|
||||
.nativeEnum(TemporaryPermissionMode)
|
||||
.describe(PROJECT_IDENTITIES.UPDATE_IDENTITY_MEMBERSHIP.roles.temporaryMode),
|
||||
temporaryRange: z
|
||||
.string()
|
||||
@@ -153,16 +160,24 @@ export const registerIdentityProjectRouter = async (server: FastifyZodProvider)
|
||||
}
|
||||
},
|
||||
handler: async (req) => {
|
||||
const roles = await server.services.identityProject.updateProjectIdentity({
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
identityId: req.params.identityId,
|
||||
projectId: req.params.projectId,
|
||||
roles: req.body.roles
|
||||
const { membership } = await server.services.membershipIdentity.updateMembership({
|
||||
permission: req.permission,
|
||||
scopeData: {
|
||||
scope: AccessScope.Project,
|
||||
orgId: req.permission.orgId,
|
||||
projectId: req.params.projectId
|
||||
},
|
||||
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) => {
|
||||
const identityMembership = await server.services.identityProject.deleteProjectIdentity({
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
identityId: req.params.identityId,
|
||||
projectId: req.params.projectId
|
||||
const { membership } = await server.services.membershipIdentity.deleteMembership({
|
||||
permission: req.permission,
|
||||
scopeData: {
|
||||
scope: AccessScope.Project,
|
||||
orgId: req.permission.orgId,
|
||||
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({
|
||||
method: "GET",
|
||||
url: "/identity-memberships/:identityMembershipId",
|
||||
|
||||
@@ -1,53 +1,45 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
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 { ormify, sqlNestRelationships } from "@app/lib/knex";
|
||||
import { sqlNestRelationships } from "@app/lib/knex";
|
||||
|
||||
export type TGroupProjectDALFactory = ReturnType<typeof groupProjectDALFactory>;
|
||||
|
||||
export const groupProjectDALFactory = (db: TDbClient) => {
|
||||
const groupProjectOrm = ormify(db, TableName.GroupProjectMembership);
|
||||
|
||||
const findByProjectId = async (projectId: string, filter?: { groupId?: string }, tx?: Knex) => {
|
||||
try {
|
||||
const docs = await (tx || db.replicaNode())(TableName.GroupProjectMembership)
|
||||
.where(`${TableName.GroupProjectMembership}.projectId`, projectId)
|
||||
const docs = await (tx || db.replicaNode())(TableName.Membership)
|
||||
.where(`${TableName.Membership}.scopeProjectId`, projectId)
|
||||
.where(`${TableName.Membership}.scope`, AccessScope.Project)
|
||||
.whereNotNull(`${TableName.Membership}.actorGroupId`)
|
||||
.where((qb) => {
|
||||
if (filter?.groupId) {
|
||||
void qb.where(`${TableName.Groups}.id`, "=", filter.groupId);
|
||||
}
|
||||
})
|
||||
.join(TableName.Groups, `${TableName.GroupProjectMembership}.groupId`, `${TableName.Groups}.id`)
|
||||
.join(
|
||||
TableName.GroupProjectMembershipRole,
|
||||
`${TableName.GroupProjectMembershipRole}.projectMembershipId`,
|
||||
`${TableName.GroupProjectMembership}.id`
|
||||
)
|
||||
.leftJoin(
|
||||
TableName.ProjectRoles,
|
||||
`${TableName.GroupProjectMembershipRole}.customRoleId`,
|
||||
`${TableName.ProjectRoles}.id`
|
||||
)
|
||||
.join(TableName.Groups, `${TableName.Membership}.actorGroupId`, `${TableName.Groups}.id`)
|
||||
.join(TableName.MembershipRole, `${TableName.MembershipRole}.membershipId`, `${TableName.Membership}.id`)
|
||||
.leftJoin(TableName.Role, `${TableName.MembershipRole}.customRoleId`, `${TableName.Role}.id`)
|
||||
.select(
|
||||
db.ref("id").withSchema(TableName.GroupProjectMembership),
|
||||
db.ref("createdAt").withSchema(TableName.GroupProjectMembership),
|
||||
db.ref("updatedAt").withSchema(TableName.GroupProjectMembership),
|
||||
db.ref("id").withSchema(TableName.Membership),
|
||||
db.ref("createdAt").withSchema(TableName.Membership),
|
||||
db.ref("updatedAt").withSchema(TableName.Membership),
|
||||
db.ref("id").as("groupId").withSchema(TableName.Groups),
|
||||
db.ref("name").as("groupName").withSchema(TableName.Groups),
|
||||
db.ref("slug").as("groupSlug").withSchema(TableName.Groups),
|
||||
db.ref("id").withSchema(TableName.GroupProjectMembership),
|
||||
db.ref("role").withSchema(TableName.GroupProjectMembershipRole),
|
||||
db.ref("id").withSchema(TableName.GroupProjectMembershipRole).as("membershipRoleId"),
|
||||
db.ref("customRoleId").withSchema(TableName.GroupProjectMembershipRole),
|
||||
db.ref("name").withSchema(TableName.ProjectRoles).as("customRoleName"),
|
||||
db.ref("slug").withSchema(TableName.ProjectRoles).as("customRoleSlug"),
|
||||
db.ref("temporaryMode").withSchema(TableName.GroupProjectMembershipRole),
|
||||
db.ref("isTemporary").withSchema(TableName.GroupProjectMembershipRole),
|
||||
db.ref("temporaryRange").withSchema(TableName.GroupProjectMembershipRole),
|
||||
db.ref("temporaryAccessStartTime").withSchema(TableName.GroupProjectMembershipRole),
|
||||
db.ref("temporaryAccessEndTime").withSchema(TableName.GroupProjectMembershipRole)
|
||||
db.ref("id").withSchema(TableName.Membership),
|
||||
db.ref("role").withSchema(TableName.MembershipRole),
|
||||
db.ref("id").withSchema(TableName.MembershipRole).as("membershipRoleId"),
|
||||
db.ref("customRoleId").withSchema(TableName.MembershipRole),
|
||||
db.ref("name").withSchema(TableName.Role).as("customRoleName"),
|
||||
db.ref("slug").withSchema(TableName.Role).as("customRoleSlug"),
|
||||
db.ref("temporaryMode").withSchema(TableName.MembershipRole),
|
||||
db.ref("isTemporary").withSchema(TableName.MembershipRole),
|
||||
db.ref("temporaryRange").withSchema(TableName.MembershipRole),
|
||||
db.ref("temporaryAccessStartTime").withSchema(TableName.MembershipRole),
|
||||
db.ref("temporaryAccessEndTime").withSchema(TableName.MembershipRole)
|
||||
);
|
||||
|
||||
const members = sqlNestRelationships({
|
||||
@@ -104,8 +96,8 @@ export const groupProjectDALFactory = (db: TDbClient) => {
|
||||
try {
|
||||
const docs = await (tx || db.replicaNode())(TableName.UserGroupMembership)
|
||||
.where(`${TableName.UserGroupMembership}.userId`, userId)
|
||||
.join(TableName.Groups, function () {
|
||||
this.on(`${TableName.UserGroupMembership}.groupId`, "=", `${TableName.Groups}.id`).andOn(
|
||||
.join(TableName.Groups, (qb) => {
|
||||
qb.on(`${TableName.UserGroupMembership}.groupId`, "=", `${TableName.Groups}.id`).andOn(
|
||||
`${TableName.Groups}.orgId`,
|
||||
"=",
|
||||
db.raw("?", [orgId])
|
||||
@@ -131,32 +123,26 @@ export const groupProjectDALFactory = (db: TDbClient) => {
|
||||
const docs = await db(TableName.UserGroupMembership)
|
||||
// Join the GroupProjectMembership table with the Groups table to get the group name and slug.
|
||||
.join(
|
||||
TableName.GroupProjectMembership,
|
||||
TableName.Membership,
|
||||
`${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.GroupProjectMembership}.projectId`, `${TableName.Project}.id`)
|
||||
|
||||
.where(`${TableName.GroupProjectMembership}.projectId`, projectId)
|
||||
|
||||
.join(TableName.Project, `${TableName.Membership}.scopeProjectId`, `${TableName.Project}.id`)
|
||||
.where(`${TableName.Membership}.scopeProjectId`, projectId)
|
||||
.where(`${TableName.Membership}.scope`, AccessScope.Project)
|
||||
.join(TableName.Users, `${TableName.UserGroupMembership}.userId`, `${TableName.Users}.id`)
|
||||
.join<TUserEncryptionKeys>(
|
||||
TableName.UserEncryptionKey,
|
||||
`${TableName.UserEncryptionKey}.userId`,
|
||||
`${TableName.Users}.id`
|
||||
)
|
||||
.join(
|
||||
TableName.GroupProjectMembershipRole,
|
||||
`${TableName.GroupProjectMembershipRole}.projectMembershipId`,
|
||||
`${TableName.GroupProjectMembership}.id`
|
||||
)
|
||||
.leftJoin(
|
||||
TableName.ProjectRoles,
|
||||
`${TableName.GroupProjectMembershipRole}.customRoleId`,
|
||||
`${TableName.ProjectRoles}.id`
|
||||
)
|
||||
.join(TableName.OrgMembership, `${TableName.Users}.id`, `${TableName.OrgMembership}.userId`)
|
||||
.join(TableName.MembershipRole, `${TableName.MembershipRole}.membershipId`, `${TableName.Membership}.id`)
|
||||
.leftJoin(TableName.Role, `${TableName.MembershipRole}.customRoleId`, `${TableName.Role}.id`)
|
||||
.join<TMemberships>(db(TableName.Membership).as("orgMembership"), (qb) => {
|
||||
qb.on(`${TableName.Users}.id`, `orgMembership.userId`)
|
||||
.andOn(`orgMembership.scope`, db.raw("?", [AccessScope.Organization]))
|
||||
.andOn(`orgMembership.scopeOrgId`, `${TableName.Project}.orgId`);
|
||||
})
|
||||
.select(
|
||||
db.ref("id").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("lastName").withSchema(TableName.Users),
|
||||
db.ref("id").withSchema(TableName.Users).as("userId"),
|
||||
db.ref("role").withSchema(TableName.GroupProjectMembershipRole),
|
||||
db.ref("id").withSchema(TableName.GroupProjectMembershipRole).as("membershipRoleId"),
|
||||
db.ref("customRoleId").withSchema(TableName.GroupProjectMembershipRole),
|
||||
db.ref("name").withSchema(TableName.ProjectRoles).as("customRoleName"),
|
||||
db.ref("slug").withSchema(TableName.ProjectRoles).as("customRoleSlug"),
|
||||
db.ref("temporaryMode").withSchema(TableName.GroupProjectMembershipRole),
|
||||
db.ref("isTemporary").withSchema(TableName.GroupProjectMembershipRole),
|
||||
db.ref("temporaryRange").withSchema(TableName.GroupProjectMembershipRole),
|
||||
db.ref("temporaryAccessStartTime").withSchema(TableName.GroupProjectMembershipRole),
|
||||
db.ref("temporaryAccessEndTime").withSchema(TableName.GroupProjectMembershipRole),
|
||||
db.ref("role").withSchema(TableName.MembershipRole),
|
||||
db.ref("id").withSchema(TableName.MembershipRole).as("membershipRoleId"),
|
||||
db.ref("customRoleId").withSchema(TableName.MembershipRole),
|
||||
db.ref("name").withSchema(TableName.Role).as("customRoleName"),
|
||||
db.ref("slug").withSchema(TableName.Role).as("customRoleSlug"),
|
||||
db.ref("temporaryMode").withSchema(TableName.MembershipRole),
|
||||
db.ref("isTemporary").withSchema(TableName.MembershipRole),
|
||||
db.ref("temporaryRange").withSchema(TableName.MembershipRole),
|
||||
db.ref("temporaryAccessStartTime").withSchema(TableName.MembershipRole),
|
||||
db.ref("temporaryAccessEndTime").withSchema(TableName.MembershipRole),
|
||||
db.ref("name").as("projectName").withSchema(TableName.Project),
|
||||
db.ref("isActive").withSchema(TableName.OrgMembership)
|
||||
db.ref("isActive").withSchema("orgMembership")
|
||||
)
|
||||
.where({ isGhost: false });
|
||||
|
||||
@@ -242,5 +228,5 @@ export const groupProjectDALFactory = (db: TDbClient) => {
|
||||
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 {
|
||||
AccessScope,
|
||||
TableName,
|
||||
TIdentities,
|
||||
TIdentityAlicloudAuths,
|
||||
@@ -15,7 +16,7 @@ import {
|
||||
TIdentityUniversalAuths
|
||||
} from "@app/db/schemas";
|
||||
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 { 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 const identityProjectDALFactory = (db: TDbClient) => {
|
||||
const identityProjectOrm = ormify(db, TableName.IdentityProjectMembership);
|
||||
|
||||
const findByIdentityId = async (identityId: string, tx?: Knex) => {
|
||||
try {
|
||||
const docs = await (tx || db.replicaNode())(TableName.IdentityProjectMembership)
|
||||
.where(`${TableName.IdentityProjectMembership}.identityId`, identityId)
|
||||
.join(TableName.Project, `${TableName.IdentityProjectMembership}.projectId`, `${TableName.Project}.id`)
|
||||
.join(TableName.Identity, `${TableName.IdentityProjectMembership}.identityId`, `${TableName.Identity}.id`)
|
||||
.join(
|
||||
TableName.IdentityProjectMembershipRole,
|
||||
`${TableName.IdentityProjectMembershipRole}.projectMembershipId`,
|
||||
`${TableName.IdentityProjectMembership}.id`
|
||||
)
|
||||
.leftJoin(
|
||||
TableName.ProjectRoles,
|
||||
`${TableName.IdentityProjectMembershipRole}.customRoleId`,
|
||||
`${TableName.ProjectRoles}.id`
|
||||
)
|
||||
.leftJoin(
|
||||
TableName.IdentityProjectAdditionalPrivilege,
|
||||
`${TableName.IdentityProjectMembership}.id`,
|
||||
`${TableName.IdentityProjectAdditionalPrivilege}.projectMembershipId`
|
||||
)
|
||||
|
||||
const docs = await (tx || db.replicaNode())(TableName.Membership)
|
||||
.where(`${TableName.Membership}.actorIdentityId`, identityId)
|
||||
.where(`${TableName.Membership}.scope`, AccessScope.Project)
|
||||
.whereNotNull(`${TableName.Membership}.actorIdentityId`)
|
||||
.join(TableName.Project, `${TableName.Membership}.scopeProjectId`, `${TableName.Project}.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.IdentityUniversalAuth,
|
||||
`${TableName.IdentityProjectMembership}.identityId`,
|
||||
`${TableName.Membership}.actorIdentityId`,
|
||||
`${TableName.IdentityUniversalAuth}.identityId`
|
||||
)
|
||||
.leftJoin(
|
||||
TableName.IdentityGcpAuth,
|
||||
`${TableName.IdentityProjectMembership}.identityId`,
|
||||
`${TableName.Membership}.actorIdentityId`,
|
||||
`${TableName.IdentityGcpAuth}.identityId`
|
||||
)
|
||||
.leftJoin(
|
||||
TableName.IdentityAliCloudAuth,
|
||||
`${TableName.IdentityProjectMembership}.identityId`,
|
||||
`${TableName.Membership}.actorIdentityId`,
|
||||
`${TableName.IdentityAliCloudAuth}.identityId`
|
||||
)
|
||||
.leftJoin(
|
||||
TableName.IdentityAwsAuth,
|
||||
`${TableName.IdentityProjectMembership}.identityId`,
|
||||
`${TableName.Membership}.actorIdentityId`,
|
||||
`${TableName.IdentityAwsAuth}.identityId`
|
||||
)
|
||||
.leftJoin(
|
||||
TableName.IdentityKubernetesAuth,
|
||||
`${TableName.IdentityProjectMembership}.identityId`,
|
||||
`${TableName.Membership}.actorIdentityId`,
|
||||
`${TableName.IdentityKubernetesAuth}.identityId`
|
||||
)
|
||||
.leftJoin(
|
||||
TableName.IdentityOciAuth,
|
||||
`${TableName.IdentityProjectMembership}.identityId`,
|
||||
`${TableName.Membership}.actorIdentityId`,
|
||||
`${TableName.IdentityOciAuth}.identityId`
|
||||
)
|
||||
.leftJoin(
|
||||
TableName.IdentityOidcAuth,
|
||||
`${TableName.IdentityProjectMembership}.identityId`,
|
||||
`${TableName.Membership}.actorIdentityId`,
|
||||
`${TableName.IdentityOidcAuth}.identityId`
|
||||
)
|
||||
.leftJoin(
|
||||
TableName.IdentityAzureAuth,
|
||||
`${TableName.IdentityProjectMembership}.identityId`,
|
||||
`${TableName.Membership}.actorIdentityId`,
|
||||
`${TableName.IdentityAzureAuth}.identityId`
|
||||
)
|
||||
.leftJoin(
|
||||
TableName.IdentityTokenAuth,
|
||||
`${TableName.IdentityProjectMembership}.identityId`,
|
||||
`${TableName.Membership}.actorIdentityId`,
|
||||
`${TableName.IdentityTokenAuth}.identityId`
|
||||
)
|
||||
|
||||
.select(
|
||||
db.ref("id").withSchema(TableName.IdentityProjectMembership),
|
||||
db.ref("createdAt").withSchema(TableName.IdentityProjectMembership),
|
||||
db.ref("updatedAt").withSchema(TableName.IdentityProjectMembership),
|
||||
db.ref("id").withSchema(TableName.Membership),
|
||||
db.ref("createdAt").withSchema(TableName.Membership),
|
||||
db.ref("updatedAt").withSchema(TableName.Membership),
|
||||
|
||||
db.ref("id").as("identityId").withSchema(TableName.Identity),
|
||||
db.ref("name").as("identityName").withSchema(TableName.Identity),
|
||||
db.ref("hasDeleteProtection").withSchema(TableName.Identity),
|
||||
db.ref("id").withSchema(TableName.IdentityProjectMembership),
|
||||
db.ref("role").withSchema(TableName.IdentityProjectMembershipRole),
|
||||
db.ref("id").withSchema(TableName.IdentityProjectMembershipRole).as("membershipRoleId"),
|
||||
db.ref("customRoleId").withSchema(TableName.IdentityProjectMembershipRole),
|
||||
db.ref("name").withSchema(TableName.ProjectRoles).as("customRoleName"),
|
||||
db.ref("slug").withSchema(TableName.ProjectRoles).as("customRoleSlug"),
|
||||
db.ref("temporaryMode").withSchema(TableName.IdentityProjectMembershipRole),
|
||||
db.ref("isTemporary").withSchema(TableName.IdentityProjectMembershipRole),
|
||||
db.ref("temporaryRange").withSchema(TableName.IdentityProjectMembershipRole),
|
||||
db.ref("temporaryAccessStartTime").withSchema(TableName.IdentityProjectMembershipRole),
|
||||
db.ref("temporaryAccessEndTime").withSchema(TableName.IdentityProjectMembershipRole),
|
||||
db.ref("projectId").withSchema(TableName.IdentityProjectMembership),
|
||||
db.ref("id").withSchema(TableName.Membership),
|
||||
db.ref("role").withSchema(TableName.MembershipRole),
|
||||
db.ref("id").withSchema(TableName.MembershipRole).as("membershipRoleId"),
|
||||
db.ref("customRoleId").withSchema(TableName.MembershipRole),
|
||||
db.ref("name").withSchema(TableName.Role).as("customRoleName"),
|
||||
db.ref("slug").withSchema(TableName.Role).as("customRoleSlug"),
|
||||
db.ref("temporaryMode").withSchema(TableName.MembershipRole),
|
||||
db.ref("isTemporary").withSchema(TableName.MembershipRole),
|
||||
db.ref("temporaryRange").withSchema(TableName.MembershipRole),
|
||||
db.ref("temporaryAccessStartTime").withSchema(TableName.MembershipRole),
|
||||
db.ref("temporaryAccessEndTime").withSchema(TableName.MembershipRole),
|
||||
db.ref("scopeProjectId").withSchema(TableName.Membership).as("projectId"),
|
||||
db.ref("name").as("projectName").withSchema(TableName.Project),
|
||||
db.ref("type").as("projectType").withSchema(TableName.Project),
|
||||
db.ref("id").as("uaId").withSchema(TableName.IdentityUniversalAuth),
|
||||
@@ -165,7 +152,7 @@ export const identityProjectDALFactory = (db: TDbClient) => {
|
||||
})
|
||||
},
|
||||
project: {
|
||||
id: projectId,
|
||||
id: projectId as string,
|
||||
name: projectName,
|
||||
type: projectType
|
||||
}
|
||||
@@ -223,12 +210,10 @@ export const identityProjectDALFactory = (db: TDbClient) => {
|
||||
void qb.whereILike(`${TableName.Identity}.name`, `%${filter.search}%`);
|
||||
}
|
||||
})
|
||||
.join(
|
||||
TableName.IdentityProjectMembership,
|
||||
`${TableName.IdentityProjectMembership}.identityId`,
|
||||
`${TableName.Identity}.id`
|
||||
)
|
||||
.where(`${TableName.IdentityProjectMembership}.projectId`, projectId)
|
||||
.join(TableName.Membership, `${TableName.Membership}.actorIdentityId`, `${TableName.Identity}.id`)
|
||||
.where(`${TableName.Membership}.scopeProjectId`, projectId)
|
||||
.where(`${TableName.Membership}.scope`, AccessScope.Project)
|
||||
.whereNotNull(`${TableName.Membership}.actorIdentityId`)
|
||||
.orderBy(
|
||||
`${TableName.Identity}.${filter.orderBy ?? ProjectIdentityOrderBy.Name}`,
|
||||
filter.orderDirection ?? OrderByDirection.ASC
|
||||
@@ -240,33 +225,20 @@ export const identityProjectDALFactory = (db: TDbClient) => {
|
||||
void fetchIdentitySubquery.offset(filter.offset ?? 0).limit(filter.limit);
|
||||
}
|
||||
|
||||
const query = (tx || db.replicaNode())(TableName.IdentityProjectMembership)
|
||||
.where(`${TableName.IdentityProjectMembership}.projectId`, projectId)
|
||||
.join(TableName.Project, `${TableName.IdentityProjectMembership}.projectId`, `${TableName.Project}.id`)
|
||||
const query = (tx || db.replicaNode())(TableName.Membership)
|
||||
.where(`${TableName.Membership}.scopeProjectId`, projectId)
|
||||
.where(`${TableName.Membership}.scope`, AccessScope.Project)
|
||||
.join(TableName.Project, `${TableName.Membership}.scopeProjectId`, `${TableName.Project}.id`)
|
||||
.join<TIdentities, TIdentities>(fetchIdentitySubquery, (bd) => {
|
||||
bd.on(`${TableName.IdentityProjectMembership}.identityId`, `${TableName.Identity}.id`);
|
||||
bd.on(`${TableName.Membership}.actorIdentityId`, `${TableName.Identity}.id`);
|
||||
})
|
||||
.where((qb) => {
|
||||
if (filter.identityId) {
|
||||
void qb.where(`${TableName.IdentityProjectMembership}.identityId`, filter.identityId);
|
||||
void qb.where(`${TableName.Membership}.actorIdentityId`, filter.identityId);
|
||||
}
|
||||
})
|
||||
.join(
|
||||
TableName.IdentityProjectMembershipRole,
|
||||
`${TableName.IdentityProjectMembershipRole}.projectMembershipId`,
|
||||
`${TableName.IdentityProjectMembership}.id`
|
||||
)
|
||||
.leftJoin(
|
||||
TableName.ProjectRoles,
|
||||
`${TableName.IdentityProjectMembershipRole}.customRoleId`,
|
||||
`${TableName.ProjectRoles}.id`
|
||||
)
|
||||
.leftJoin(
|
||||
TableName.IdentityProjectAdditionalPrivilege,
|
||||
`${TableName.IdentityProjectMembership}.id`,
|
||||
`${TableName.IdentityProjectAdditionalPrivilege}.projectMembershipId`
|
||||
)
|
||||
|
||||
.join(TableName.MembershipRole, `${TableName.MembershipRole}.membershipId`, `${TableName.Membership}.id`)
|
||||
.leftJoin(TableName.Role, `${TableName.MembershipRole}.customRoleId`, `${TableName.Role}.id`)
|
||||
.leftJoin<TIdentityUniversalAuths>(
|
||||
TableName.IdentityUniversalAuth,
|
||||
`${TableName.Identity}.id`,
|
||||
@@ -314,23 +286,23 @@ export const identityProjectDALFactory = (db: TDbClient) => {
|
||||
)
|
||||
|
||||
.select(
|
||||
db.ref("id").withSchema(TableName.IdentityProjectMembership),
|
||||
db.ref("createdAt").withSchema(TableName.IdentityProjectMembership),
|
||||
db.ref("updatedAt").withSchema(TableName.IdentityProjectMembership),
|
||||
db.ref("id").withSchema(TableName.Membership),
|
||||
db.ref("createdAt").withSchema(TableName.Membership),
|
||||
db.ref("updatedAt").withSchema(TableName.Membership),
|
||||
db.ref("authMethod").as("identityAuthMethod").withSchema(TableName.Identity),
|
||||
db.ref("id").as("identityId").withSchema(TableName.Identity),
|
||||
db.ref("name").as("identityName").withSchema(TableName.Identity),
|
||||
db.ref("id").withSchema(TableName.IdentityProjectMembership),
|
||||
db.ref("role").withSchema(TableName.IdentityProjectMembershipRole),
|
||||
db.ref("id").withSchema(TableName.IdentityProjectMembershipRole).as("membershipRoleId"),
|
||||
db.ref("customRoleId").withSchema(TableName.IdentityProjectMembershipRole),
|
||||
db.ref("name").withSchema(TableName.ProjectRoles).as("customRoleName"),
|
||||
db.ref("slug").withSchema(TableName.ProjectRoles).as("customRoleSlug"),
|
||||
db.ref("temporaryMode").withSchema(TableName.IdentityProjectMembershipRole),
|
||||
db.ref("isTemporary").withSchema(TableName.IdentityProjectMembershipRole),
|
||||
db.ref("temporaryRange").withSchema(TableName.IdentityProjectMembershipRole),
|
||||
db.ref("temporaryAccessStartTime").withSchema(TableName.IdentityProjectMembershipRole),
|
||||
db.ref("temporaryAccessEndTime").withSchema(TableName.IdentityProjectMembershipRole),
|
||||
db.ref("id").withSchema(TableName.Membership),
|
||||
db.ref("role").withSchema(TableName.MembershipRole),
|
||||
db.ref("id").withSchema(TableName.MembershipRole).as("membershipRoleId"),
|
||||
db.ref("customRoleId").withSchema(TableName.MembershipRole),
|
||||
db.ref("name").withSchema(TableName.Role).as("customRoleName"),
|
||||
db.ref("slug").withSchema(TableName.Role).as("customRoleSlug"),
|
||||
db.ref("temporaryMode").withSchema(TableName.MembershipRole),
|
||||
db.ref("isTemporary").withSchema(TableName.MembershipRole),
|
||||
db.ref("temporaryRange").withSchema(TableName.MembershipRole),
|
||||
db.ref("temporaryAccessStartTime").withSchema(TableName.MembershipRole),
|
||||
db.ref("temporaryAccessEndTime").withSchema(TableName.MembershipRole),
|
||||
db.ref("name").as("projectName").withSchema(TableName.Project),
|
||||
db.ref("id").as("uaId").withSchema(TableName.IdentityUniversalAuth),
|
||||
db.ref("id").as("gcpId").withSchema(TableName.IdentityGcpAuth),
|
||||
@@ -450,10 +422,11 @@ export const identityProjectDALFactory = (db: TDbClient) => {
|
||||
tx?: Knex
|
||||
) => {
|
||||
try {
|
||||
const identities = await (tx || db.replicaNode())(TableName.IdentityProjectMembership)
|
||||
.where(`${TableName.IdentityProjectMembership}.projectId`, projectId)
|
||||
.join(TableName.Project, `${TableName.IdentityProjectMembership}.projectId`, `${TableName.Project}.id`)
|
||||
.join(TableName.Identity, `${TableName.IdentityProjectMembership}.identityId`, `${TableName.Identity}.id`)
|
||||
const identities = await (tx || db.replicaNode())(TableName.Membership)
|
||||
.where(`${TableName.Membership}.scopeProjectId`, projectId)
|
||||
.where(`${TableName.Membership}.scope`, AccessScope.Project)
|
||||
.join(TableName.Project, `${TableName.Membership}.scopeProjectId`, `${TableName.Project}.id`)
|
||||
.join(TableName.Identity, `${TableName.Membership}.actorIdentityId`, `${TableName.Identity}.id`)
|
||||
.where((qb) => {
|
||||
if (filter.identityId) {
|
||||
void qb.where("identityId", filter.identityId);
|
||||
@@ -472,7 +445,6 @@ export const identityProjectDALFactory = (db: TDbClient) => {
|
||||
};
|
||||
|
||||
return {
|
||||
...identityProjectOrm,
|
||||
findByIdentityId,
|
||||
findByProjectId,
|
||||
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;
|
||||
};
|
||||
@@ -2,6 +2,7 @@ import { Knex } from "knex";
|
||||
|
||||
import { TDbClient } from "@app/db";
|
||||
import {
|
||||
AccessScope,
|
||||
TableName,
|
||||
TIdentityAlicloudAuths,
|
||||
TIdentityAwsAuths,
|
||||
@@ -11,15 +12,15 @@ import {
|
||||
TIdentityKubernetesAuths,
|
||||
TIdentityOciAuths,
|
||||
TIdentityOidcAuths,
|
||||
TIdentityOrgMemberships,
|
||||
TIdentityTlsCertAuths,
|
||||
TIdentityTokenAuths,
|
||||
TIdentityUniversalAuths,
|
||||
TOrgRoles
|
||||
TMembershipRoles,
|
||||
TMemberships
|
||||
} from "@app/db/schemas";
|
||||
import { TIdentityLdapAuths } from "@app/db/schemas/identity-ldap-auths";
|
||||
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 { OrderByDirection } from "@app/lib/types";
|
||||
import {
|
||||
@@ -33,80 +34,80 @@ import { buildAuthMethods } from "./identity-fns";
|
||||
export type TIdentityOrgDALFactory = ReturnType<typeof identityOrgDALFactory>;
|
||||
|
||||
export const identityOrgDALFactory = (db: TDbClient) => {
|
||||
const identityOrgOrm = ormify(db, TableName.IdentityOrgMembership);
|
||||
|
||||
const findOne = async (filter: Partial<TIdentityOrgMemberships>, tx?: Knex) => {
|
||||
const findOne = async (filter: Partial<TMemberships>, tx?: Knex) => {
|
||||
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) => {
|
||||
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>(
|
||||
TableName.IdentityUniversalAuth,
|
||||
`${TableName.IdentityOrgMembership}.identityId`,
|
||||
`${TableName.Membership}.actorIdentityId`,
|
||||
`${TableName.IdentityUniversalAuth}.identityId`
|
||||
)
|
||||
.leftJoin<TIdentityGcpAuths>(
|
||||
TableName.IdentityGcpAuth,
|
||||
`${TableName.IdentityOrgMembership}.identityId`,
|
||||
`${TableName.Membership}.actorIdentityId`,
|
||||
`${TableName.IdentityGcpAuth}.identityId`
|
||||
)
|
||||
.leftJoin<TIdentityAlicloudAuths>(
|
||||
TableName.IdentityAliCloudAuth,
|
||||
`${TableName.IdentityOrgMembership}.identityId`,
|
||||
`${TableName.Membership}.actorIdentityId`,
|
||||
`${TableName.IdentityAliCloudAuth}.identityId`
|
||||
)
|
||||
.leftJoin<TIdentityAwsAuths>(
|
||||
TableName.IdentityAwsAuth,
|
||||
`${TableName.IdentityOrgMembership}.identityId`,
|
||||
`${TableName.Membership}.actorIdentityId`,
|
||||
`${TableName.IdentityAwsAuth}.identityId`
|
||||
)
|
||||
.leftJoin<TIdentityKubernetesAuths>(
|
||||
TableName.IdentityKubernetesAuth,
|
||||
`${TableName.IdentityOrgMembership}.identityId`,
|
||||
`${TableName.Membership}.actorIdentityId`,
|
||||
`${TableName.IdentityKubernetesAuth}.identityId`
|
||||
)
|
||||
.leftJoin<TIdentityOciAuths>(
|
||||
TableName.IdentityOciAuth,
|
||||
`${TableName.IdentityOrgMembership}.identityId`,
|
||||
`${TableName.Membership}.actorIdentityId`,
|
||||
`${TableName.IdentityOciAuth}.identityId`
|
||||
)
|
||||
.leftJoin<TIdentityOidcAuths>(
|
||||
TableName.IdentityOidcAuth,
|
||||
`${TableName.IdentityOrgMembership}.identityId`,
|
||||
`${TableName.Membership}.actorIdentityId`,
|
||||
`${TableName.IdentityOidcAuth}.identityId`
|
||||
)
|
||||
.leftJoin<TIdentityAzureAuths>(
|
||||
TableName.IdentityAzureAuth,
|
||||
`${TableName.IdentityOrgMembership}.identityId`,
|
||||
`${TableName.Membership}.actorIdentityId`,
|
||||
`${TableName.IdentityAzureAuth}.identityId`
|
||||
)
|
||||
.leftJoin<TIdentityTokenAuths>(
|
||||
TableName.IdentityTokenAuth,
|
||||
`${TableName.IdentityOrgMembership}.identityId`,
|
||||
`${TableName.Membership}.actorIdentityId`,
|
||||
`${TableName.IdentityTokenAuth}.identityId`
|
||||
)
|
||||
.leftJoin<TIdentityJwtAuths>(
|
||||
TableName.IdentityJwtAuth,
|
||||
`${TableName.IdentityOrgMembership}.identityId`,
|
||||
`${TableName.Membership}.actorIdentityId`,
|
||||
`${TableName.IdentityJwtAuth}.identityId`
|
||||
)
|
||||
.leftJoin<TIdentityLdapAuths>(
|
||||
TableName.IdentityLdapAuth,
|
||||
`${TableName.IdentityOrgMembership}.identityId`,
|
||||
`${TableName.Membership}.actorIdentityId`,
|
||||
`${TableName.IdentityLdapAuth}.identityId`
|
||||
)
|
||||
.leftJoin<TIdentityTlsCertAuths>(
|
||||
TableName.IdentityTlsCertAuth,
|
||||
`${TableName.IdentityOrgMembership}.identityId`,
|
||||
`${TableName.Membership}.actorIdentityId`,
|
||||
`${TableName.IdentityTlsCertAuth}.identityId`
|
||||
)
|
||||
.select(
|
||||
selectAllTableCols(TableName.IdentityOrgMembership),
|
||||
selectAllTableCols(TableName.Membership),
|
||||
|
||||
db.ref("id").as("uaId").withSchema(TableName.IdentityUniversalAuth),
|
||||
db.ref("id").as("gcpId").withSchema(TableName.IdentityGcpAuth),
|
||||
@@ -129,7 +130,7 @@ export const identityOrgDALFactory = (db: TDbClient) => {
|
||||
return {
|
||||
...data,
|
||||
identity: {
|
||||
id: data.identityId,
|
||||
id: data.actorIdentityId as string,
|
||||
name,
|
||||
hasDeleteProtection,
|
||||
authMethods: buildAuthMethods(data)
|
||||
@@ -149,20 +150,18 @@ export const identityOrgDALFactory = (db: TDbClient) => {
|
||||
orderDirection = OrderByDirection.ASC,
|
||||
search,
|
||||
...filter
|
||||
}: Partial<TIdentityOrgMemberships> &
|
||||
}: Partial<TMemberships> &
|
||||
Pick<TListOrgIdentitiesByOrgIdDTO, "offset" | "limit" | "orderBy" | "orderDirection" | "search">,
|
||||
tx?: Knex
|
||||
) => {
|
||||
try {
|
||||
const paginatedIdentity = (tx || db.replicaNode())(TableName.Identity)
|
||||
.join(
|
||||
TableName.IdentityOrgMembership,
|
||||
`${TableName.IdentityOrgMembership}.identityId`,
|
||||
`${TableName.Identity}.id`
|
||||
)
|
||||
.join(TableName.Membership, `${TableName.Membership}.actorIdentityId`, `${TableName.Identity}.id`)
|
||||
.where(`${TableName.Membership}.scope`, AccessScope.Organization)
|
||||
.whereNotNull(`${TableName.Membership}.actorIdentityId`)
|
||||
.orderBy(`${TableName.Identity}.${orderBy}`, orderDirection)
|
||||
.select(
|
||||
selectAllTableCols(TableName.IdentityOrgMembership),
|
||||
selectAllTableCols(TableName.Membership),
|
||||
db.ref("name").withSchema(TableName.Identity).as("identityName"),
|
||||
db.ref("hasDeleteProtection").withSchema(TableName.Identity)
|
||||
)
|
||||
@@ -181,14 +180,17 @@ export const identityOrgDALFactory = (db: TDbClient) => {
|
||||
type TSubquery = Awaited<typeof paginatedIdentity>;
|
||||
const query = (tx || db.replicaNode())
|
||||
.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) => {
|
||||
void queryBuilder
|
||||
.on(`paginatedIdentity.identityId`, `${TableName.IdentityMetadata}.identityId`)
|
||||
.andOn(`paginatedIdentity.orgId`, `${TableName.IdentityMetadata}.orgId`);
|
||||
.on(`paginatedIdentity.actorIdentityId`, `${TableName.IdentityMetadata}.identityId`)
|
||||
.andOn(`paginatedIdentity.scopeOrgId`, `${TableName.IdentityMetadata}.orgId`);
|
||||
})
|
||||
|
||||
.leftJoin<TIdentityUniversalAuths>(
|
||||
TableName.IdentityUniversalAuth,
|
||||
"paginatedIdentity.identityId",
|
||||
@@ -251,14 +253,14 @@ export const identityOrgDALFactory = (db: TDbClient) => {
|
||||
)
|
||||
.select(
|
||||
db.ref("id").withSchema("paginatedIdentity"),
|
||||
db.ref("role").withSchema("paginatedIdentity"),
|
||||
db.ref("roleId").withSchema("paginatedIdentity"),
|
||||
db.ref("orgId").withSchema("paginatedIdentity"),
|
||||
db.ref("role").withSchema(TableName.MembershipRole),
|
||||
db.ref("customRoleId").withSchema(TableName.MembershipRole).as("roleId"),
|
||||
db.ref("scopeOrgId").withSchema("paginatedIdentity").as("orgId"),
|
||||
db.ref("lastLoginAuthMethod").withSchema("paginatedIdentity"),
|
||||
db.ref("lastLoginTime").withSchema("paginatedIdentity"),
|
||||
db.ref("createdAt").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("hasDeleteProtection").withSchema("paginatedIdentity"),
|
||||
|
||||
@@ -276,12 +278,11 @@ export const identityOrgDALFactory = (db: TDbClient) => {
|
||||
db.ref("id").as("tlsCertId").withSchema(TableName.IdentityTlsCertAuth)
|
||||
)
|
||||
// cr stands for custom role
|
||||
.select(db.ref("id").as("crId").withSchema(TableName.OrgRoles))
|
||||
.select(db.ref("name").as("crName").withSchema(TableName.OrgRoles))
|
||||
.select(db.ref("slug").as("crSlug").withSchema(TableName.OrgRoles))
|
||||
.select(db.ref("description").as("crDescription").withSchema(TableName.OrgRoles))
|
||||
.select(db.ref("permissions").as("crPermission").withSchema(TableName.OrgRoles))
|
||||
.select(db.ref("permissions").as("crPermission").withSchema(TableName.OrgRoles))
|
||||
.select(db.ref("id").as("crId").withSchema(TableName.Role))
|
||||
.select(db.ref("name").as("crName").withSchema(TableName.Role))
|
||||
.select(db.ref("slug").as("crSlug").withSchema(TableName.Role))
|
||||
.select(db.ref("description").as("crDescription").withSchema(TableName.Role))
|
||||
.select(db.ref("permissions").as("crPermission").withSchema(TableName.Role))
|
||||
.select(
|
||||
db.ref("id").withSchema(TableName.IdentityMetadata).as("metadataId"),
|
||||
db.ref("key").withSchema(TableName.IdentityMetadata).as("metadataKey"),
|
||||
@@ -327,7 +328,7 @@ export const identityOrgDALFactory = (db: TDbClient) => {
|
||||
}) => ({
|
||||
role,
|
||||
roleId,
|
||||
identityId,
|
||||
identityId: identityId as string,
|
||||
id,
|
||||
orgId,
|
||||
createdAt,
|
||||
@@ -344,7 +345,7 @@ export const identityOrgDALFactory = (db: TDbClient) => {
|
||||
}
|
||||
: undefined,
|
||||
identity: {
|
||||
id: identityId,
|
||||
id: identityId as string,
|
||||
name: identityName,
|
||||
hasDeleteProtection,
|
||||
authMethods: buildAuthMethods({
|
||||
@@ -394,20 +395,23 @@ export const identityOrgDALFactory = (db: TDbClient) => {
|
||||
tx?: Knex
|
||||
) => {
|
||||
try {
|
||||
const searchQuery = (tx || db.replicaNode())(TableName.IdentityOrgMembership)
|
||||
.join(TableName.Identity, `${TableName.Identity}.id`, `${TableName.IdentityOrgMembership}.identityId`)
|
||||
.where(`${TableName.IdentityOrgMembership}.orgId`, orgId)
|
||||
.leftJoin(TableName.OrgRoles, `${TableName.IdentityOrgMembership}.roleId`, `${TableName.OrgRoles}.id`)
|
||||
const searchQuery = (tx || db.replicaNode())(TableName.Membership)
|
||||
.where(`${TableName.Membership}.scope`, AccessScope.Organization)
|
||||
.whereNotNull(`${TableName.Membership}.actorIdentityId`)
|
||||
.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 === OrgIdentityOrderBy.Role
|
||||
? `${TableName.IdentityOrgMembership}.${orderBy}`
|
||||
? `${TableName.Membership}.${orderBy}`
|
||||
: `${TableName.Identity}.${orderBy}`,
|
||||
orderDirection
|
||||
)
|
||||
.select(`${TableName.IdentityOrgMembership}.id`)
|
||||
.select(`${TableName.Membership}.id`)
|
||||
.select<{ id: string; total_count: string }>(
|
||||
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");
|
||||
@@ -416,7 +420,7 @@ export const identityOrgDALFactory = (db: TDbClient) => {
|
||||
buildKnexFilterForSearchResource(searchQuery, searchFilter, (attr) => {
|
||||
switch (attr) {
|
||||
case "role":
|
||||
return [`${TableName.OrgRoles}.slug`, `${TableName.IdentityOrgMembership}.role`];
|
||||
return [`${TableName.Role}.slug`, `${TableName.MembershipRole}.role`];
|
||||
case "name":
|
||||
return `${TableName.Identity}.name`;
|
||||
default:
|
||||
@@ -430,82 +434,85 @@ export const identityOrgDALFactory = (db: TDbClient) => {
|
||||
}
|
||||
|
||||
type TSubquery = Awaited<typeof searchQuery>;
|
||||
const query = (tx || db.replicaNode())(TableName.IdentityOrgMembership)
|
||||
.where(`${TableName.IdentityOrgMembership}.orgId`, orgId)
|
||||
.join<TSubquery>(searchQuery, `${TableName.IdentityOrgMembership}.id`, "searchedIdentities.id")
|
||||
.join(TableName.Identity, `${TableName.IdentityOrgMembership}.identityId`, `${TableName.Identity}.id`)
|
||||
.leftJoin(TableName.OrgRoles, `${TableName.IdentityOrgMembership}.roleId`, `${TableName.OrgRoles}.id`)
|
||||
const query = (tx || db.replicaNode())(TableName.Membership)
|
||||
.where(`${TableName.Membership}.scope`, AccessScope.Organization)
|
||||
.whereNotNull(`${TableName.Membership}.actorIdentityId`)
|
||||
.where(`${TableName.Membership}.scopeOrgId`, orgId)
|
||||
.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) => {
|
||||
void queryBuilder
|
||||
.on(`${TableName.IdentityOrgMembership}.identityId`, `${TableName.IdentityMetadata}.identityId`)
|
||||
.andOn(`${TableName.IdentityOrgMembership}.orgId`, `${TableName.IdentityMetadata}.orgId`);
|
||||
.on(`${TableName.Membership}.actorIdentityId`, `${TableName.IdentityMetadata}.identityId`)
|
||||
.andOn(`${TableName.Membership}.scopeOrgId`, `${TableName.IdentityMetadata}.orgId`);
|
||||
})
|
||||
.leftJoin(
|
||||
TableName.IdentityUniversalAuth,
|
||||
`${TableName.IdentityOrgMembership}.identityId`,
|
||||
`${TableName.Membership}.actorIdentityId`,
|
||||
`${TableName.IdentityUniversalAuth}.identityId`
|
||||
)
|
||||
.leftJoin(
|
||||
TableName.IdentityGcpAuth,
|
||||
`${TableName.IdentityOrgMembership}.identityId`,
|
||||
`${TableName.Membership}.actorIdentityId`,
|
||||
`${TableName.IdentityGcpAuth}.identityId`
|
||||
)
|
||||
.leftJoin(
|
||||
TableName.IdentityAliCloudAuth,
|
||||
`${TableName.IdentityOrgMembership}.identityId`,
|
||||
`${TableName.Membership}.actorIdentityId`,
|
||||
`${TableName.IdentityAliCloudAuth}.identityId`
|
||||
)
|
||||
.leftJoin(
|
||||
TableName.IdentityAwsAuth,
|
||||
`${TableName.IdentityOrgMembership}.identityId`,
|
||||
`${TableName.Membership}.actorIdentityId`,
|
||||
`${TableName.IdentityAwsAuth}.identityId`
|
||||
)
|
||||
.leftJoin(
|
||||
TableName.IdentityKubernetesAuth,
|
||||
`${TableName.IdentityOrgMembership}.identityId`,
|
||||
`${TableName.Membership}.actorIdentityId`,
|
||||
`${TableName.IdentityKubernetesAuth}.identityId`
|
||||
)
|
||||
.leftJoin(
|
||||
TableName.IdentityOciAuth,
|
||||
`${TableName.IdentityOrgMembership}.identityId`,
|
||||
`${TableName.Membership}.actorIdentityId`,
|
||||
`${TableName.IdentityOciAuth}.identityId`
|
||||
)
|
||||
.leftJoin(
|
||||
TableName.IdentityOidcAuth,
|
||||
`${TableName.IdentityOrgMembership}.identityId`,
|
||||
`${TableName.Membership}.actorIdentityId`,
|
||||
`${TableName.IdentityOidcAuth}.identityId`
|
||||
)
|
||||
.leftJoin(
|
||||
TableName.IdentityAzureAuth,
|
||||
`${TableName.IdentityOrgMembership}.identityId`,
|
||||
`${TableName.Membership}.actorIdentityId`,
|
||||
`${TableName.IdentityAzureAuth}.identityId`
|
||||
)
|
||||
.leftJoin(
|
||||
TableName.IdentityTokenAuth,
|
||||
`${TableName.IdentityOrgMembership}.identityId`,
|
||||
`${TableName.Membership}.actorIdentityId`,
|
||||
`${TableName.IdentityTokenAuth}.identityId`
|
||||
)
|
||||
.leftJoin(
|
||||
TableName.IdentityJwtAuth,
|
||||
`${TableName.IdentityOrgMembership}.identityId`,
|
||||
`${TableName.Membership}.actorIdentityId`,
|
||||
`${TableName.IdentityJwtAuth}.identityId`
|
||||
)
|
||||
.leftJoin(
|
||||
TableName.IdentityLdapAuth,
|
||||
`${TableName.IdentityOrgMembership}.identityId`,
|
||||
`${TableName.Membership}.actorIdentityId`,
|
||||
`${TableName.IdentityLdapAuth}.identityId`
|
||||
)
|
||||
.select(
|
||||
db.ref("id").withSchema(TableName.IdentityOrgMembership),
|
||||
db.ref("id").withSchema(TableName.Membership),
|
||||
db.ref("total_count").withSchema("searchedIdentities"),
|
||||
db.ref("role").withSchema(TableName.IdentityOrgMembership),
|
||||
db.ref("roleId").withSchema(TableName.IdentityOrgMembership),
|
||||
db.ref("orgId").withSchema(TableName.IdentityOrgMembership),
|
||||
db.ref("createdAt").withSchema(TableName.IdentityOrgMembership),
|
||||
db.ref("updatedAt").withSchema(TableName.IdentityOrgMembership),
|
||||
db.ref("lastLoginAuthMethod").withSchema(TableName.IdentityOrgMembership),
|
||||
db.ref("lastLoginTime").withSchema(TableName.IdentityOrgMembership),
|
||||
db.ref("identityId").withSchema(TableName.IdentityOrgMembership).as("identityId"),
|
||||
db.ref("role").withSchema(TableName.MembershipRole),
|
||||
db.ref("customRoleId").withSchema(TableName.MembershipRole).as("roleId"),
|
||||
db.ref("scopeOrgId").withSchema(TableName.Membership).as("orgId"),
|
||||
db.ref("createdAt").withSchema(TableName.Membership),
|
||||
db.ref("updatedAt").withSchema(TableName.Membership),
|
||||
db.ref("lastLoginAuthMethod").withSchema(TableName.Membership),
|
||||
db.ref("lastLoginTime").withSchema(TableName.Membership),
|
||||
db.ref("actorIdentityId").withSchema(TableName.Membership).as("identityId"),
|
||||
db.ref("name").withSchema(TableName.Identity).as("identityName"),
|
||||
db.ref("hasDeleteProtection").withSchema(TableName.Identity),
|
||||
|
||||
@@ -522,12 +529,11 @@ export const identityOrgDALFactory = (db: TDbClient) => {
|
||||
db.ref("id").as("ldapId").withSchema(TableName.IdentityLdapAuth)
|
||||
)
|
||||
// cr stands for custom role
|
||||
.select(db.ref("id").as("crId").withSchema(TableName.OrgRoles))
|
||||
.select(db.ref("name").as("crName").withSchema(TableName.OrgRoles))
|
||||
.select(db.ref("slug").as("crSlug").withSchema(TableName.OrgRoles))
|
||||
.select(db.ref("description").as("crDescription").withSchema(TableName.OrgRoles))
|
||||
.select(db.ref("permissions").as("crPermission").withSchema(TableName.OrgRoles))
|
||||
.select(db.ref("permissions").as("crPermission").withSchema(TableName.OrgRoles))
|
||||
.select(db.ref("id").as("crId").withSchema(TableName.Role))
|
||||
.select(db.ref("name").as("crName").withSchema(TableName.Role))
|
||||
.select(db.ref("slug").as("crSlug").withSchema(TableName.Role))
|
||||
.select(db.ref("description").as("crDescription").withSchema(TableName.Role))
|
||||
.select(db.ref("permissions").as("crPermission").withSchema(TableName.Role))
|
||||
.select(
|
||||
db.ref("id").withSchema(TableName.IdentityMetadata).as("metadataId"),
|
||||
db.ref("key").withSchema(TableName.IdentityMetadata).as("metadataKey"),
|
||||
@@ -545,13 +551,7 @@ export const identityOrgDALFactory = (db: TDbClient) => {
|
||||
ELSE ??.role
|
||||
END ?
|
||||
`,
|
||||
[
|
||||
TableName.IdentityOrgMembership,
|
||||
"custom",
|
||||
TableName.OrgRoles,
|
||||
TableName.IdentityOrgMembership,
|
||||
db.raw(orderDirection)
|
||||
]
|
||||
[TableName.MembershipRole, "custom", TableName.Role, TableName.MembershipRole, db.raw(orderDirection)]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -590,7 +590,7 @@ export const identityOrgDALFactory = (db: TDbClient) => {
|
||||
}) => ({
|
||||
role,
|
||||
roleId,
|
||||
identityId,
|
||||
identityId: identityId as string,
|
||||
id,
|
||||
total_count: total_count as string,
|
||||
orgId,
|
||||
@@ -646,13 +646,15 @@ export const identityOrgDALFactory = (db: TDbClient) => {
|
||||
};
|
||||
|
||||
const countAllOrgIdentities = async (
|
||||
{ search, ...filter }: Partial<TIdentityOrgMemberships> & Pick<TListOrgIdentitiesByOrgIdDTO, "search">,
|
||||
{ search, ...filter }: Partial<TMemberships> & Pick<TListOrgIdentitiesByOrgIdDTO, "search">,
|
||||
tx?: Knex
|
||||
) => {
|
||||
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)
|
||||
.join(TableName.Identity, `${TableName.IdentityOrgMembership}.identityId`, `${TableName.Identity}.id`)
|
||||
.join(TableName.Identity, `${TableName.Membership}.actorIdentityId`, `${TableName.Identity}.id`)
|
||||
.count();
|
||||
|
||||
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
|
||||
);
|
||||
await membershipRoleDAL.insertMany(roleDocs, tx);
|
||||
return doc;
|
||||
const insertedRoleDocs = await membershipRoleDAL.insertMany(roleDocs, tx);
|
||||
return { ...doc, roles: insertedRoleDocs };
|
||||
});
|
||||
|
||||
return { membership: membershipDoc };
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
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 { ormify, sqlNestRelationships } from "@app/lib/knex";
|
||||
import { sqlNestRelationships } from "@app/lib/knex";
|
||||
|
||||
export type TOrgMembershipDALFactory = ReturnType<typeof orgMembershipDALFactory>;
|
||||
|
||||
export const orgMembershipDALFactory = (db: TDbClient) => {
|
||||
const orgMembershipOrm = ormify(db, TableName.OrgMembership);
|
||||
|
||||
const findOrgMembershipById = async (membershipId: string) => {
|
||||
try {
|
||||
const member = await db
|
||||
.replicaNode()(TableName.OrgMembership)
|
||||
.where(`${TableName.OrgMembership}.id`, membershipId)
|
||||
.join(TableName.Users, `${TableName.OrgMembership}.userId`, `${TableName.Users}.id`)
|
||||
.replicaNode()(TableName.Membership)
|
||||
.where(`${TableName.Membership}.id`, membershipId)
|
||||
.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>(
|
||||
TableName.UserEncryptionKey,
|
||||
`${TableName.UserEncryptionKey}.userId`,
|
||||
@@ -21,19 +22,19 @@ export const orgMembershipDALFactory = (db: TDbClient) => {
|
||||
)
|
||||
.leftJoin(TableName.IdentityMetadata, (queryBuilder) => {
|
||||
void queryBuilder
|
||||
.on(`${TableName.OrgMembership}.userId`, `${TableName.IdentityMetadata}.userId`)
|
||||
.andOn(`${TableName.OrgMembership}.orgId`, `${TableName.IdentityMetadata}.orgId`);
|
||||
.on(`${TableName.Membership}.actorUserId`, `${TableName.IdentityMetadata}.userId`)
|
||||
.andOn(`${TableName.Membership}.scopeOrgId`, `${TableName.IdentityMetadata}.orgId`);
|
||||
})
|
||||
.select(
|
||||
db.ref("id").withSchema(TableName.OrgMembership),
|
||||
db.ref("inviteEmail").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("isActive").withSchema(TableName.OrgMembership),
|
||||
db.ref("lastLoginAuthMethod").withSchema(TableName.OrgMembership),
|
||||
db.ref("lastLoginTime").withSchema(TableName.OrgMembership),
|
||||
db.ref("id").withSchema(TableName.Membership),
|
||||
db.ref("inviteEmail").withSchema(TableName.Membership),
|
||||
db.ref("scopeOrgId").withSchema(TableName.Membership).as("orgId"),
|
||||
db.ref("role").withSchema(TableName.MembershipRole),
|
||||
db.ref("customRoleId").withSchema(TableName.MembershipRole).as("roleId"),
|
||||
db.ref("status").withSchema(TableName.Membership),
|
||||
db.ref("isActive").withSchema(TableName.Membership),
|
||||
db.ref("lastLoginAuthMethod").withSchema(TableName.Membership),
|
||||
db.ref("lastLoginTime").withSchema(TableName.Membership),
|
||||
db.ref("email").withSchema(TableName.Users),
|
||||
db.ref("username").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 memberships = await db
|
||||
.replicaNode()(TableName.OrgMembership)
|
||||
.replicaNode()(TableName.Membership)
|
||||
.where(`${TableName.Membership}.scope`, AccessScope.Organization)
|
||||
.whereNotNull(`${TableName.Membership}.actorUserId`)
|
||||
.where("status", "invited")
|
||||
.where((qb) => {
|
||||
// lastInvitedAt is null AND createdAt is between 1 week and 12 months ago
|
||||
void qb
|
||||
.whereNull(`${TableName.OrgMembership}.lastInvitedAt`)
|
||||
.whereBetween(`${TableName.OrgMembership}.createdAt`, [twelveMonthsAgo, oneWeekAgo]);
|
||||
.whereNull(`${TableName.Membership}.lastInvitedAt`)
|
||||
.whereBetween(`${TableName.Membership}.createdAt`, [twelveMonthsAgo, oneWeekAgo]);
|
||||
// lastInvitedAt is older than 1 week ago AND createdAt is younger than 1 month ago
|
||||
void qb.orWhere((qbInner) => {
|
||||
void qbInner
|
||||
.where(`${TableName.OrgMembership}.lastInvitedAt`, "<", oneWeekAgo)
|
||||
.where(`${TableName.OrgMembership}.createdAt`, ">", oneMonthAgo);
|
||||
.where(`${TableName.Membership}.lastInvitedAt`, "<", oneWeekAgo)
|
||||
.where(`${TableName.Membership}.createdAt`, ">", oneMonthAgo);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -144,7 +147,11 @@ export const orgMembershipDALFactory = (db: TDbClient) => {
|
||||
const updateLastInvitedAtByIds = async (membershipIds: string[]) => {
|
||||
try {
|
||||
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) {
|
||||
throw new DatabaseError({
|
||||
error,
|
||||
@@ -156,9 +163,12 @@ export const orgMembershipDALFactory = (db: TDbClient) => {
|
||||
const findOrgMembershipsWithUsersByOrgId = async (orgId: string) => {
|
||||
try {
|
||||
const members = await db
|
||||
.replicaNode()(TableName.OrgMembership)
|
||||
.where(`${TableName.OrgMembership}.orgId`, orgId)
|
||||
.join(TableName.Users, `${TableName.OrgMembership}.userId`, `${TableName.Users}.id`)
|
||||
.replicaNode()(TableName.Membership)
|
||||
.where(`${TableName.Membership}.scopeOrgId`, orgId)
|
||||
.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>(
|
||||
TableName.UserEncryptionKey,
|
||||
`${TableName.UserEncryptionKey}.userId`,
|
||||
@@ -166,17 +176,17 @@ export const orgMembershipDALFactory = (db: TDbClient) => {
|
||||
)
|
||||
.leftJoin(TableName.IdentityMetadata, (queryBuilder) => {
|
||||
void queryBuilder
|
||||
.on(`${TableName.OrgMembership}.userId`, `${TableName.IdentityMetadata}.userId`)
|
||||
.andOn(`${TableName.OrgMembership}.orgId`, `${TableName.IdentityMetadata}.orgId`);
|
||||
.on(`${TableName.Membership}.actorUserId`, `${TableName.IdentityMetadata}.userId`)
|
||||
.andOn(`${TableName.Membership}.scopeOrgId`, `${TableName.IdentityMetadata}.orgId`);
|
||||
})
|
||||
.select(
|
||||
db.ref("id").withSchema(TableName.OrgMembership),
|
||||
db.ref("inviteEmail").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("isActive").withSchema(TableName.OrgMembership),
|
||||
db.ref("id").withSchema(TableName.Membership),
|
||||
db.ref("inviteEmail").withSchema(TableName.Membership),
|
||||
db.ref("scopeOrgId").withSchema(TableName.Membership).as("orgId"),
|
||||
db.ref("role").withSchema(TableName.MembershipRole),
|
||||
db.ref("customRoleId").withSchema(TableName.MembershipRole).as("customRoleId"),
|
||||
db.ref("status").withSchema(TableName.Membership),
|
||||
db.ref("isActive").withSchema(TableName.Membership),
|
||||
db.ref("email").withSchema(TableName.Users),
|
||||
db.ref("username").withSchema(TableName.Users),
|
||||
db.ref("firstName").withSchema(TableName.Users),
|
||||
@@ -207,7 +217,6 @@ export const orgMembershipDALFactory = (db: TDbClient) => {
|
||||
};
|
||||
|
||||
return {
|
||||
...orgMembershipOrm,
|
||||
findOrgMembershipById,
|
||||
findRecentInvitedMemberships,
|
||||
updateLastInvitedAtByIds,
|
||||
|
||||
@@ -2,14 +2,15 @@ import { Knex } from "knex";
|
||||
|
||||
import { TDbClient } from "@app/db";
|
||||
import {
|
||||
AccessScope,
|
||||
OrganizationsSchema,
|
||||
OrgMembershipRole,
|
||||
TableName,
|
||||
TMemberships,
|
||||
TMembershipsInsert,
|
||||
TMembershipsUpdate,
|
||||
TOrganizations,
|
||||
TOrganizationsInsert,
|
||||
TOrgMemberships,
|
||||
TOrgMembershipsInsert,
|
||||
TOrgMembershipsUpdate,
|
||||
TUserEncryptionKeys
|
||||
} from "@app/db/schemas";
|
||||
import { DatabaseError } from "@app/lib/errors";
|
||||
@@ -64,9 +65,12 @@ export const orgDALFactory = (db: TDbClient) => {
|
||||
.replicaNode()(TableName.Organization)
|
||||
.whereIn(`${TableName.Organization}.id`, orgIdSubquery)
|
||||
.leftJoin(TableName.Project, `${TableName.Organization}.id`, `${TableName.Project}.orgId`)
|
||||
.leftJoin(TableName.OrgMembership, `${TableName.Organization}.id`, `${TableName.OrgMembership}.orgId`)
|
||||
.leftJoin(TableName.Users, `${TableName.OrgMembership}.userId`, `${TableName.Users}.id`)
|
||||
.leftJoin(TableName.OrgRoles, `${TableName.OrgMembership}.roleId`, `${TableName.OrgRoles}.id`)
|
||||
.leftJoin(TableName.Membership, `${TableName.Organization}.id`, `${TableName.Membership}.scopeOrgId`)
|
||||
.where(`${TableName.Membership}.scope`, AccessScope.Organization)
|
||||
.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) => {
|
||||
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("lastName").withSchema(TableName.Users).as("lastName"))
|
||||
.select(db.ref("id").withSchema(TableName.Users).as("userId"))
|
||||
.select(db.ref("id").withSchema(TableName.OrgMembership).as("orgMembershipId"))
|
||||
.select(db.ref("role").withSchema(TableName.OrgMembership).as("orgMembershipRole"))
|
||||
.select(db.ref("roleId").withSchema(TableName.OrgMembership).as("orgMembershipRoleId"))
|
||||
.select(db.ref("status").withSchema(TableName.OrgMembership).as("orgMembershipStatus"))
|
||||
.select(db.ref("name").withSchema(TableName.OrgRoles).as("orgMembershipRoleName")),
|
||||
.select(db.ref("id").withSchema(TableName.Membership).as("orgMembershipId"))
|
||||
.select(db.ref("status").withSchema(TableName.Membership).as("orgMembershipStatus"))
|
||||
.select(db.ref("role").withSchema(TableName.MembershipRole).as("orgMembershipRole"))
|
||||
.select(db.ref("customRoleId").withSchema(TableName.MembershipRole).as("orgMembershipRoleId"))
|
||||
.select(db.ref("name").withSchema(TableName.Role).as("orgMembershipRoleName")),
|
||||
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 })[]> => {
|
||||
try {
|
||||
const org = (await db
|
||||
.replicaNode()(TableName.OrgMembership)
|
||||
.where({ userId })
|
||||
.join(TableName.Organization, `${TableName.OrgMembership}.orgId`, `${TableName.Organization}.id`)
|
||||
.replicaNode()(TableName.Membership)
|
||||
.where(`${TableName.Membership}.actorUserId`, userId)
|
||||
.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) => {
|
||||
qb.on(`${TableName.SamlConfig}.orgId`, "=", `${TableName.Organization}.id`).andOn(
|
||||
`${TableName.SamlConfig}.isActive`,
|
||||
@@ -247,8 +254,8 @@ export const orgDALFactory = (db: TDbClient) => {
|
||||
);
|
||||
})
|
||||
.select(selectAllTableCols(TableName.Organization))
|
||||
.select(db.ref("role").withSchema(TableName.OrgMembership).as("userRole"))
|
||||
.select(db.ref("status").withSchema(TableName.OrgMembership).as("userStatus"))
|
||||
.select(db.ref("role").withSchema(TableName.MembershipRole).as("userRole"))
|
||||
.select(db.ref("status").withSchema(TableName.Membership).as("userStatus"))
|
||||
.select(
|
||||
db.raw(`
|
||||
CASE
|
||||
@@ -283,24 +290,28 @@ export const orgDALFactory = (db: TDbClient) => {
|
||||
const findAllOrgMembers = async (orgId: string) => {
|
||||
try {
|
||||
const members = await db
|
||||
.replicaNode()(TableName.OrgMembership)
|
||||
.where(`${TableName.OrgMembership}.orgId`, orgId)
|
||||
.join(TableName.Users, `${TableName.OrgMembership}.userId`, `${TableName.Users}.id`)
|
||||
.replicaNode()(TableName.Membership)
|
||||
.where(`${TableName.Membership}.scopeOrgId`, orgId)
|
||||
.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>(
|
||||
TableName.UserEncryptionKey,
|
||||
`${TableName.UserEncryptionKey}.userId`,
|
||||
`${TableName.Users}.id`
|
||||
)
|
||||
.select(
|
||||
db.ref("id").withSchema(TableName.OrgMembership),
|
||||
db.ref("inviteEmail").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("isActive").withSchema(TableName.OrgMembership),
|
||||
db.ref("lastLoginAuthMethod").withSchema(TableName.OrgMembership),
|
||||
db.ref("lastLoginTime").withSchema(TableName.OrgMembership),
|
||||
db.ref("id").withSchema(TableName.Membership),
|
||||
db.ref("inviteEmail").withSchema(TableName.Membership),
|
||||
db.ref("actorOrgId").withSchema(TableName.Membership).as("orgId"),
|
||||
db.ref("role").withSchema(TableName.MembershipRole),
|
||||
db.ref("customRoleId").withSchema(TableName.MembershipRole).as("roleId"),
|
||||
db.ref("status").withSchema(TableName.Membership),
|
||||
db.ref("isActive").withSchema(TableName.Membership),
|
||||
db.ref("lastLoginAuthMethod").withSchema(TableName.Membership),
|
||||
db.ref("lastLoginTime").withSchema(TableName.Membership),
|
||||
db.ref("email").withSchema(TableName.Users),
|
||||
db.ref("isEmailVerified").withSchema(TableName.Users),
|
||||
db.ref("username").withSchema(TableName.Users),
|
||||
@@ -332,11 +343,13 @@ export const orgDALFactory = (db: TDbClient) => {
|
||||
}
|
||||
|
||||
const count = await db
|
||||
.replicaNode()(TableName.OrgMembership)
|
||||
.where(`${TableName.OrgMembership}.orgId`, orgId)
|
||||
.replicaNode()(TableName.Membership)
|
||||
.where(`${TableName.Membership}.orgId`, orgId)
|
||||
.where(`${TableName.Membership}.scope`, AccessScope.Organization)
|
||||
.whereNotNull(`${TableName.Membership}.actorUserId`)
|
||||
.count("*")
|
||||
.join(TableName.Users, `${TableName.OrgMembership}.userId`, `${TableName.Users}.id`)
|
||||
.where({ isGhost: false, [`${TableName.OrgMembership}.isActive` as "isActive"]: true })
|
||||
.join(TableName.Users, `${TableName.Membership}.actorUserId`, `${TableName.Users}.id`)
|
||||
.where({ isGhost: false, [`${TableName.Membership}.isActive` as "isActive"]: true })
|
||||
.first();
|
||||
|
||||
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) => {
|
||||
try {
|
||||
const conn = tx || db.replicaNode();
|
||||
const members = await conn(TableName.OrgMembership)
|
||||
.where(`${TableName.OrgMembership}.orgId`, orgId)
|
||||
.join(TableName.Users, `${TableName.OrgMembership}.userId`, `${TableName.Users}.id`)
|
||||
const members = await conn(TableName.Membership)
|
||||
.where(`${TableName.Membership}.scopeOrgId`, orgId)
|
||||
.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>(
|
||||
TableName.UserEncryptionKey,
|
||||
`${TableName.UserEncryptionKey}.userId`,
|
||||
`${TableName.Users}.id`
|
||||
)
|
||||
.select(
|
||||
conn.ref("id").withSchema(TableName.OrgMembership),
|
||||
conn.ref("inviteEmail").withSchema(TableName.OrgMembership),
|
||||
conn.ref("orgId").withSchema(TableName.OrgMembership),
|
||||
conn.ref("role").withSchema(TableName.OrgMembership),
|
||||
conn.ref("roleId").withSchema(TableName.OrgMembership),
|
||||
conn.ref("status").withSchema(TableName.OrgMembership),
|
||||
conn.ref("id").withSchema(TableName.Membership),
|
||||
conn.ref("inviteEmail").withSchema(TableName.Membership),
|
||||
conn.ref("orgId").withSchema(TableName.Membership),
|
||||
db.ref("role").withSchema(TableName.MembershipRole),
|
||||
db.ref("customRoleId").withSchema(TableName.MembershipRole).as("roleId"),
|
||||
conn.ref("status").withSchema(TableName.Membership),
|
||||
conn.ref("username").withSchema(TableName.Users),
|
||||
conn.ref("email").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) => {
|
||||
try {
|
||||
const conn = tx || db.replicaNode();
|
||||
const members = await conn(TableName.OrgMembership)
|
||||
.where(`${TableName.OrgMembership}.orgId`, orgId)
|
||||
.where(`${TableName.OrgMembership}.role`, role)
|
||||
.join(TableName.Users, `${TableName.OrgMembership}.userId`, `${TableName.Users}.id`)
|
||||
const members = await conn(TableName.Membership)
|
||||
.where(`${TableName.Membership}.scopeOrgId`, orgId)
|
||||
.where(`${TableName.Membership}.scope`, AccessScope.Organization)
|
||||
.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>(
|
||||
TableName.UserEncryptionKey,
|
||||
`${TableName.UserEncryptionKey}.userId`,
|
||||
`${TableName.Users}.id`
|
||||
)
|
||||
.select(
|
||||
conn.ref("id").withSchema(TableName.OrgMembership),
|
||||
conn.ref("inviteEmail").withSchema(TableName.OrgMembership),
|
||||
conn.ref("orgId").withSchema(TableName.OrgMembership),
|
||||
conn.ref("role").withSchema(TableName.OrgMembership),
|
||||
conn.ref("roleId").withSchema(TableName.OrgMembership),
|
||||
conn.ref("status").withSchema(TableName.OrgMembership),
|
||||
conn.ref("id").withSchema(TableName.Membership),
|
||||
conn.ref("inviteEmail").withSchema(TableName.Membership),
|
||||
conn.ref("orgId").withSchema(TableName.Membership),
|
||||
conn.ref("role").withSchema(TableName.MembershipRole),
|
||||
conn.ref("customRoleId").withSchema(TableName.MembershipRole).as("roleId"),
|
||||
conn.ref("status").withSchema(TableName.Membership),
|
||||
conn.ref("username").withSchema(TableName.Users),
|
||||
conn.ref("email").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) => {
|
||||
try {
|
||||
const [organization] = await (tx || db)(TableName.Organization).insert(dto).returning("*");
|
||||
@@ -491,38 +470,37 @@ export const orgDALFactory = (db: TDbClient) => {
|
||||
|
||||
// MEMBERSHIP OPERATIONS
|
||||
// --------------------
|
||||
// const orgMembershipOrm = ormify(db, TableName.OrgMembership);
|
||||
|
||||
const createMembership = async (data: TOrgMembershipsInsert, tx?: Knex) => {
|
||||
const createMembership = async (data: TMembershipsInsert, tx?: Knex) => {
|
||||
try {
|
||||
const [membership] = await (tx || db)(TableName.OrgMembership).insert(data).returning("*");
|
||||
const [membership] = await (tx || db)(TableName.Membership).insert(data).returning("*");
|
||||
return membership;
|
||||
} catch (error) {
|
||||
throw new DatabaseError({ error, name: "Create org membership" });
|
||||
}
|
||||
};
|
||||
|
||||
const bulkCreateMemberships = async (data: TOrgMembershipsInsert[], tx?: Knex) => {
|
||||
const bulkCreateMemberships = async (data: TMembershipsInsert[], tx?: Knex) => {
|
||||
try {
|
||||
const memberships = await (tx || db)(TableName.OrgMembership).insert(data).returning("*");
|
||||
const memberships = await (tx || db)(TableName.Membership).insert(data).returning("*");
|
||||
return memberships;
|
||||
} catch (error) {
|
||||
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 {
|
||||
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;
|
||||
} catch (error) {
|
||||
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 {
|
||||
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;
|
||||
} catch (error) {
|
||||
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) => {
|
||||
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;
|
||||
} catch (error) {
|
||||
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) => {
|
||||
try {
|
||||
const memberships = await (tx || db)(TableName.OrgMembership)
|
||||
const memberships = await (tx || db)(TableName.Membership)
|
||||
.where({
|
||||
orgId
|
||||
scopeOrgId: orgId,
|
||||
scope: AccessScope.Organization
|
||||
})
|
||||
.whereIn("id", ids)
|
||||
.delete()
|
||||
@@ -554,22 +536,23 @@ export const orgDALFactory = (db: TDbClient) => {
|
||||
};
|
||||
|
||||
const findMembership = async (
|
||||
filter: TFindFilter<TOrgMemberships>,
|
||||
{ offset, limit, sort, tx }: TFindOpt<TOrgMemberships> = {}
|
||||
filter: TFindFilter<TMemberships>,
|
||||
{ offset, limit, sort, tx }: TFindOpt<TMemberships> = {}
|
||||
) => {
|
||||
try {
|
||||
const query = (tx || db.replicaNode())(TableName.OrgMembership)
|
||||
const query = (tx || db.replicaNode())(TableName.Membership)
|
||||
// eslint-disable-next-line
|
||||
.where(buildFindFilter(filter))
|
||||
.join(TableName.Users, `${TableName.Users}.id`, `${TableName.OrgMembership}.userId`)
|
||||
.join(TableName.Organization, `${TableName.Organization}.id`, `${TableName.OrgMembership}.orgId`)
|
||||
.where("scope", AccessScope.Organization)
|
||||
.join(TableName.Users, `${TableName.Users}.id`, `${TableName.Membership}.actorUserId`)
|
||||
.join(TableName.Organization, `${TableName.Organization}.id`, `${TableName.Membership}.scopeOrgId`)
|
||||
.leftJoin(TableName.UserAliases, function joinUserAlias() {
|
||||
this.on(`${TableName.UserAliases}.userId`, "=", `${TableName.OrgMembership}.userId`)
|
||||
.andOn(`${TableName.UserAliases}.orgId`, "=", `${TableName.OrgMembership}.orgId`)
|
||||
this.on(`${TableName.UserAliases}.userId`, "=", `${TableName.Membership}.actorUserId`)
|
||||
.andOn(`${TableName.UserAliases}.orgId`, "=", `${TableName.Membership}.scopeOrgId`)
|
||||
.andOn(`${TableName.UserAliases}.aliasType`, "=", (tx || db).raw("?", ["saml"]));
|
||||
})
|
||||
.select(
|
||||
selectAllTableCols(TableName.OrgMembership),
|
||||
selectAllTableCols(TableName.Membership),
|
||||
db.ref("email").withSchema(TableName.Users),
|
||||
db.ref("isEmailVerified").withSchema(TableName.Users),
|
||||
db.ref("username").withSchema(TableName.Users),
|
||||
@@ -595,18 +578,20 @@ export const orgDALFactory = (db: TDbClient) => {
|
||||
const findMembershipWithScimFilter = async (
|
||||
orgId: string,
|
||||
scimFilter: string | undefined,
|
||||
{ offset, limit, sort, tx }: TFindOpt<TOrgMemberships> = {}
|
||||
{ offset, limit, sort, tx }: TFindOpt<TMemberships> = {}
|
||||
) => {
|
||||
try {
|
||||
const query = (tx || db.replicaNode())(TableName.OrgMembership)
|
||||
const query = (tx || db.replicaNode())(TableName.Membership)
|
||||
// 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) => {
|
||||
if (scimFilter) {
|
||||
void generateKnexQueryFromScim(qb, scimFilter, (attrPath) => {
|
||||
switch (attrPath) {
|
||||
case "active":
|
||||
return `${TableName.OrgMembership}.isActive`;
|
||||
return `${TableName.Membership}.isActive`;
|
||||
case "userName":
|
||||
return `${TableName.UserAliases}.externalId`;
|
||||
case "name.givenName":
|
||||
@@ -621,15 +606,15 @@ export const orgDALFactory = (db: TDbClient) => {
|
||||
});
|
||||
}
|
||||
})
|
||||
.join(TableName.Users, `${TableName.Users}.id`, `${TableName.OrgMembership}.userId`)
|
||||
.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() {
|
||||
this.on(`${TableName.UserAliases}.userId`, "=", `${TableName.OrgMembership}.userId`)
|
||||
.andOn(`${TableName.UserAliases}.orgId`, "=", `${TableName.OrgMembership}.orgId`)
|
||||
this.on(`${TableName.UserAliases}.userId`, "=", `${TableName.Membership}.actorUserId`)
|
||||
.andOn(`${TableName.UserAliases}.orgId`, "=", `${TableName.Membership}.scopeOrgId`)
|
||||
.andOn(`${TableName.UserAliases}.aliasType`, "=", (tx || db).raw("?", ["saml"]));
|
||||
})
|
||||
.select(
|
||||
selectAllTableCols(TableName.OrgMembership),
|
||||
selectAllTableCols(TableName.Membership),
|
||||
db.ref("email").withSchema(TableName.Users),
|
||||
db.ref("isEmailVerified").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 (
|
||||
identityId: string
|
||||
): Promise<{ id: string; name: string; slug: string; role: string }> => {
|
||||
try {
|
||||
const org = await db
|
||||
.replicaNode()(TableName.IdentityOrgMembership)
|
||||
.where({ identityId })
|
||||
.join(TableName.Organization, `${TableName.IdentityOrgMembership}.orgId`, `${TableName.Organization}.id`)
|
||||
.replicaNode()(TableName.Membership)
|
||||
.where({ actorIdentityId: identityId })
|
||||
.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("name").withSchema(TableName.Organization).as("name"))
|
||||
.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];
|
||||
} catch (error) {
|
||||
@@ -681,10 +670,8 @@ export const orgDALFactory = (db: TDbClient) => {
|
||||
findOrgBySlug,
|
||||
findAllOrgsByUserId,
|
||||
findOrganizationsByFilter,
|
||||
ghostUserExists,
|
||||
findOrgMembersByUsername,
|
||||
findOrgMembersByRole,
|
||||
findOrgGhostUser,
|
||||
create,
|
||||
updateById,
|
||||
deleteById,
|
||||
|
||||
@@ -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);
|
||||
@@ -1,7 +1,8 @@
|
||||
import { OrgMembershipRole } from "@app/db/schemas";
|
||||
import { TFeatureSet } from "@app/ee/services/license/license-types";
|
||||
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");
|
||||
|
||||
@@ -10,13 +11,13 @@ export const isCustomOrgRole = (roleSlug: string) => !RESERVED_ORG_ROLE_SLUGS.fi
|
||||
// this is only for updating an org
|
||||
export const getDefaultOrgMembershipRoleForUpdateOrg = async ({
|
||||
membershipRoleSlug,
|
||||
orgRoleDAL,
|
||||
roleDAL,
|
||||
plan,
|
||||
orgId
|
||||
}: {
|
||||
orgId: string;
|
||||
membershipRoleSlug: string;
|
||||
orgRoleDAL: TOrgRoleDALFactory;
|
||||
roleDAL: TRoleDALFactory;
|
||||
plan: TFeatureSet;
|
||||
}) => {
|
||||
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."
|
||||
});
|
||||
|
||||
const customRole = await orgRoleDAL.findOne({ slug: membershipRoleSlug, orgId });
|
||||
const customRole = await roleDAL.findOne({ slug: membershipRoleSlug, orgId });
|
||||
if (!customRole) {
|
||||
throw new NotFoundError({
|
||||
name: "UpdateOrg",
|
||||
|
||||
@@ -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 { 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";
|
||||
|
||||
type TOrgRoleServiceFactoryDep = {
|
||||
@@ -224,13 +224,14 @@ export const orgRoleServiceFactory = ({
|
||||
actorAuthMethod: ActorAuthMethod,
|
||||
actorOrgId: string | undefined
|
||||
) => {
|
||||
const { permission, membership } = await permissionService.getUserOrgPermission(
|
||||
const { permission, memberships } = await permissionService.getOrgPermission(
|
||||
ActorType.USER,
|
||||
userId,
|
||||
orgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
return { permissions: packRules(permission.rules), membership };
|
||||
return { permissions: packRules(permission.rules), memberships };
|
||||
};
|
||||
|
||||
return { createRole, getRole, updateRole, deleteRole, listRoles, getUserPermission };
|
||||
|
||||
@@ -76,7 +76,6 @@ import { TIncidentContactsDALFactory } from "./incident-contacts-dal";
|
||||
import { TOrgBotDALFactory } from "./org-bot-dal";
|
||||
import { TOrgDALFactory } from "./org-dal";
|
||||
import { deleteOrgMembershipFn, deleteOrgMembershipsFn } from "./org-fns";
|
||||
import { TOrgRoleDALFactory } from "./org-role-dal";
|
||||
import {
|
||||
TDeleteOrgMembershipDTO,
|
||||
TDeleteOrgMembershipsDTO,
|
||||
@@ -92,6 +91,7 @@ import {
|
||||
TUpgradePrivilegeSystemDTO,
|
||||
TVerifyUserToOrgDTO
|
||||
} from "./org-types";
|
||||
import { TRoleDALFactory } from "../role/role-dal";
|
||||
|
||||
type TOrgServiceFactoryDep = {
|
||||
userAliasDAL: Pick<TUserAliasDALFactory, "delete">;
|
||||
@@ -100,7 +100,7 @@ type TOrgServiceFactoryDep = {
|
||||
folderDAL: Pick<TSecretFolderDALFactory, "findByProjectId">;
|
||||
orgDAL: TOrgDALFactory;
|
||||
orgBotDAL: TOrgBotDALFactory;
|
||||
orgRoleDAL: TOrgRoleDALFactory;
|
||||
roleDAL: TRoleDALFactory;
|
||||
userDAL: TUserDALFactory;
|
||||
groupDAL: TGroupDALFactory;
|
||||
projectDAL: TProjectDALFactory;
|
||||
@@ -154,7 +154,7 @@ export const orgServiceFactory = ({
|
||||
folderDAL,
|
||||
userDAL,
|
||||
groupDAL,
|
||||
orgRoleDAL,
|
||||
roleDAL,
|
||||
incidentContactDAL,
|
||||
permissionService,
|
||||
smtpService,
|
||||
@@ -185,7 +185,7 @@ export const orgServiceFactory = ({
|
||||
actorAuthMethod: ActorAuthMethod,
|
||||
actorOrgId: string | undefined
|
||||
) => {
|
||||
await permissionService.getUserOrgPermission(userId, orgId, actorAuthMethod, actorOrgId);
|
||||
await permissionService.getOrgPermission(ActorType.USER, userId, orgId, actorAuthMethod, actorOrgId);
|
||||
const appCfg = getConfig();
|
||||
const org = await orgDAL.findOrgById(orgId);
|
||||
if (!org) throw new NotFoundError({ message: `Organization with ID '${orgId}' not found` });
|
||||
@@ -221,7 +221,13 @@ export const orgServiceFactory = ({
|
||||
actorAuthMethod: ActorAuthMethod,
|
||||
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);
|
||||
|
||||
const members = await orgDAL.findAllOrgMembers(orgId);
|
||||
@@ -323,9 +329,15 @@ export const orgServiceFactory = ({
|
||||
actorAuthMethod,
|
||||
orgId
|
||||
}: 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({
|
||||
message: "Insufficient privileges - only the organization admin can upgrade the privilege system."
|
||||
});
|
||||
@@ -531,7 +543,7 @@ export const orgServiceFactory = ({
|
||||
defaultMembershipRole = await getDefaultOrgMembershipRoleForUpdateOrg({
|
||||
membershipRoleSlug: defaultMembershipRoleSlug,
|
||||
orgId,
|
||||
orgRoleDAL,
|
||||
roleDAL,
|
||||
plan
|
||||
});
|
||||
}
|
||||
@@ -659,8 +671,14 @@ export const orgServiceFactory = ({
|
||||
actorAuthMethod: ActorAuthMethod;
|
||||
actorOrgId: string | undefined;
|
||||
}) => {
|
||||
const { membership } = await permissionService.getUserOrgPermission(userId, orgId, actorAuthMethod, actorOrgId);
|
||||
if ((membership.role as OrgMembershipRole) !== OrgMembershipRole.Admin) {
|
||||
const { hasRole } = await permissionService.getOrgPermission(
|
||||
ActorType.USER,
|
||||
userId,
|
||||
orgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
if (!hasRole(OrgMembershipRole.Admin)) {
|
||||
throw new ForbiddenRequestError({
|
||||
name: "DeleteOrganizationById",
|
||||
message: "Insufficient privileges"
|
||||
@@ -739,7 +757,13 @@ export const orgServiceFactory = ({
|
||||
actorOrgId,
|
||||
metadata
|
||||
}: 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);
|
||||
|
||||
const foundMembership = await orgMembershipDAL.findById(membershipId);
|
||||
@@ -754,7 +778,7 @@ export const orgServiceFactory = ({
|
||||
let userRole = role;
|
||||
let userRoleId: string | null = null;
|
||||
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" });
|
||||
|
||||
const plan = await licenseService.getPlan(orgId);
|
||||
@@ -768,16 +792,16 @@ export const orgServiceFactory = ({
|
||||
}
|
||||
const membership = await orgDAL.transaction(async (tx) => {
|
||||
const [updatedOrgMembership] = await orgDAL.updateMembership(
|
||||
{ id: membershipId, orgId },
|
||||
{ id: membershipId, scopeOrgId: orgId },
|
||||
{ role: userRole, roleId: userRoleId, isActive }
|
||||
);
|
||||
|
||||
if (metadata) {
|
||||
await identityMetadataDAL.delete({ userId: updatedOrgMembership.userId, orgId }, tx);
|
||||
await identityMetadataDAL.delete({ userId: updatedOrgMembership.actorUserId, orgId }, tx);
|
||||
if (metadata.length) {
|
||||
await identityMetadataDAL.insertMany(
|
||||
metadata.map(({ key, value }) => ({
|
||||
userId: updatedOrgMembership.userId,
|
||||
userId: updatedOrgMembership.actorUserId as string,
|
||||
orgId,
|
||||
key,
|
||||
value
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
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 { ormify, selectAllTableCols } from "@app/lib/knex";
|
||||
|
||||
@@ -44,12 +44,13 @@ export const projectBotDALFactory = (db: TDbClient) => {
|
||||
const findProjectUserWorkspaceKey = async (projectId: string) => {
|
||||
try {
|
||||
const doc = await db
|
||||
.replicaNode()(TableName.ProjectMembership)
|
||||
.where(`${TableName.ProjectMembership}.projectId` as "projectId", projectId)
|
||||
.where(`${TableName.ProjectKeys}.projectId` as "projectId", projectId)
|
||||
.replicaNode()(TableName.Membership)
|
||||
.where(`${TableName.Membership}.scopeProjectId` as "projectId", projectId)
|
||||
.where(`${TableName.Membership}.scope`, AccessScope.Project)
|
||||
.where(`${TableName.Users}.isGhost` as "isGhost", false)
|
||||
.join(TableName.Users, `${TableName.ProjectMembership}.userId`, `${TableName.Users}.id`)
|
||||
.join(TableName.ProjectKeys, `${TableName.ProjectMembership}.userId`, `${TableName.ProjectKeys}.receiverId`)
|
||||
.join(TableName.Users, `${TableName.Membership}.actorUserId`, `${TableName.Users}.id`)
|
||||
.join(TableName.ProjectKeys, `${TableName.Membership}.actorUserId`, `${TableName.ProjectKeys}.receiverId`)
|
||||
.where(`${TableName.ProjectKeys}.projectId` as "projectId", projectId)
|
||||
.join<TUserEncryptionKeys>(
|
||||
TableName.UserEncryptionKey,
|
||||
`${TableName.UserEncryptionKey}.userId`,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
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 { ormify, selectAllTableCols } from "@app/lib/knex";
|
||||
|
||||
@@ -34,9 +34,10 @@ export const projectKeyDALFactory = (db: TDbClient) => {
|
||||
|
||||
const findAllProjectUserPubKeys = async (projectId: string, tx?: Knex) => {
|
||||
try {
|
||||
const pubKeys = await (tx || db.replicaNode())(TableName.ProjectMembership)
|
||||
.where({ projectId })
|
||||
.join(TableName.Users, `${TableName.ProjectMembership}.userId`, `${TableName.Users}.id`)
|
||||
const pubKeys = await (tx || db.replicaNode())(TableName.Membership)
|
||||
.where(`${TableName.Membership}.scopeProjectId` as "projectId", projectId)
|
||||
.where(`${TableName.Membership}.scope`, AccessScope.Project)
|
||||
.join(TableName.Users, `${TableName.Membership}.actorUserId`, `${TableName.Users}.id`)
|
||||
.join(TableName.UserEncryptionKey, `${TableName.Users}.id`, `${TableName.UserEncryptionKey}.userId`)
|
||||
.select("userId", "publicKey");
|
||||
return pubKeys;
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
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 { ormify, selectAllTableCols, sqlNestRelationships } from "@app/lib/knex";
|
||||
import { selectAllTableCols, sqlNestRelationships } from "@app/lib/knex";
|
||||
|
||||
export type TProjectMembershipDALFactory = ReturnType<typeof projectMembershipDALFactory>;
|
||||
|
||||
export const projectMembershipDALFactory = (db: TDbClient) => {
|
||||
const projectMemberOrm = ormify(db, TableName.ProjectMembership);
|
||||
|
||||
// special query
|
||||
const findAllProjectMembers = async (
|
||||
projectId: string,
|
||||
@@ -17,18 +15,17 @@ export const projectMembershipDALFactory = (db: TDbClient) => {
|
||||
) => {
|
||||
try {
|
||||
const docs = await db
|
||||
.replicaNode()(TableName.ProjectMembership)
|
||||
.where({ [`${TableName.ProjectMembership}.projectId` as "projectId"]: projectId })
|
||||
.join(TableName.Project, `${TableName.ProjectMembership}.projectId`, `${TableName.Project}.id`)
|
||||
.join(TableName.Users, `${TableName.ProjectMembership}.userId`, `${TableName.Users}.id`)
|
||||
.join(TableName.OrgMembership, (qb) => {
|
||||
qb.on(`${TableName.Users}.id`, "=", `${TableName.OrgMembership}.userId`).andOn(
|
||||
`${TableName.OrgMembership}.orgId`,
|
||||
"=",
|
||||
`${TableName.Project}.orgId`
|
||||
);
|
||||
.replicaNode()(TableName.Membership)
|
||||
.where({ [`${TableName.Membership}.scopeProjectId` as "scopeProjectId"]: projectId })
|
||||
.where({ [`${TableName.Membership}.scope` as "scope"]: AccessScope.Project })
|
||||
.whereNotNull(`${TableName.Membership}.actorUserId`)
|
||||
.join(TableName.Project, `${TableName.Membership}.scopeProjectId`, `${TableName.Project}.id`)
|
||||
.join(TableName.Users, `${TableName.Membership}.actorUserId`, `${TableName.Users}.id`)
|
||||
.join<TMemberships>(db(TableName.Membership).as("orgMembership"), (qb) => {
|
||||
qb.on(`${TableName.Users}.id`, "=", `orgMembership.actorUserId`)
|
||||
.andOn(`orgMembership.scopeOrgId`, "=", `${TableName.Project}.orgId`)
|
||||
.andOn("orgMembership.scope", AccessScope.Organization);
|
||||
})
|
||||
|
||||
.where((qb) => {
|
||||
if (filter.usernames) {
|
||||
void qb.whereIn("username", filter.usernames);
|
||||
@@ -37,28 +34,24 @@ export const projectMembershipDALFactory = (db: TDbClient) => {
|
||||
void qb.where("username", filter.username);
|
||||
}
|
||||
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) {
|
||||
void qb.whereExists((subQuery) => {
|
||||
void subQuery
|
||||
.select("role")
|
||||
.from(TableName.ProjectUserMembershipRole)
|
||||
.leftJoin(
|
||||
TableName.ProjectRoles,
|
||||
`${TableName.ProjectRoles}.id`,
|
||||
`${TableName.ProjectUserMembershipRole}.customRoleId`
|
||||
)
|
||||
.from(TableName.MembershipRole)
|
||||
.leftJoin(TableName.Role, `${TableName.Role}.id`, `${TableName.MembershipRole}.customRoleId`)
|
||||
.whereRaw("??.?? = ??.??", [
|
||||
TableName.ProjectUserMembershipRole,
|
||||
TableName.MembershipRole,
|
||||
"projectMembershipId",
|
||||
TableName.ProjectMembership,
|
||||
TableName.Membership,
|
||||
"id"
|
||||
])
|
||||
.where((subQb) => {
|
||||
void subQb
|
||||
.whereIn(`${TableName.ProjectUserMembershipRole}.role`, filter.roles as string[])
|
||||
.orWhereIn(`${TableName.ProjectRoles}.slug`, filter.roles as string[]);
|
||||
.whereIn(`${TableName.MembershipRole}.role`, 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.Users}.id`
|
||||
)
|
||||
.join(
|
||||
TableName.ProjectUserMembershipRole,
|
||||
`${TableName.ProjectUserMembershipRole}.projectMembershipId`,
|
||||
`${TableName.ProjectMembership}.id`
|
||||
)
|
||||
.leftJoin(
|
||||
TableName.ProjectRoles,
|
||||
`${TableName.ProjectUserMembershipRole}.customRoleId`,
|
||||
`${TableName.ProjectRoles}.id`
|
||||
)
|
||||
.join(TableName.MembershipRole, `${TableName.MembershipRole}.membershipId`, `${TableName.Membership}.id`)
|
||||
.leftJoin(TableName.Role, `${TableName.MembershipRole}.customRoleId`, `${TableName.Role}.id`)
|
||||
.select(
|
||||
db.ref("id").withSchema(TableName.ProjectMembership),
|
||||
db.ref("createdAt").withSchema(TableName.ProjectMembership),
|
||||
db.ref("id").withSchema(TableName.Membership),
|
||||
db.ref("createdAt").withSchema(TableName.Membership),
|
||||
db.ref("isGhost").withSchema(TableName.Users),
|
||||
db.ref("username").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("lastName").withSchema(TableName.Users),
|
||||
db.ref("id").withSchema(TableName.Users).as("userId"),
|
||||
db.ref("role").withSchema(TableName.ProjectUserMembershipRole),
|
||||
db.ref("id").withSchema(TableName.ProjectUserMembershipRole).as("membershipRoleId"),
|
||||
db.ref("customRoleId").withSchema(TableName.ProjectUserMembershipRole),
|
||||
db.ref("name").withSchema(TableName.ProjectRoles).as("customRoleName"),
|
||||
db.ref("slug").withSchema(TableName.ProjectRoles).as("customRoleSlug"),
|
||||
db.ref("temporaryMode").withSchema(TableName.ProjectUserMembershipRole),
|
||||
db.ref("isTemporary").withSchema(TableName.ProjectUserMembershipRole),
|
||||
db.ref("temporaryRange").withSchema(TableName.ProjectUserMembershipRole),
|
||||
db.ref("temporaryAccessStartTime").withSchema(TableName.ProjectUserMembershipRole),
|
||||
db.ref("temporaryAccessEndTime").withSchema(TableName.ProjectUserMembershipRole),
|
||||
db.ref("role").withSchema(TableName.MembershipRole),
|
||||
db.ref("id").withSchema(TableName.MembershipRole).as("membershipRoleId"),
|
||||
db.ref("customRoleId").withSchema(TableName.MembershipRole),
|
||||
db.ref("name").withSchema(TableName.Role).as("customRoleName"),
|
||||
db.ref("slug").withSchema(TableName.Role).as("customRoleSlug"),
|
||||
db.ref("temporaryMode").withSchema(TableName.MembershipRole),
|
||||
db.ref("isTemporary").withSchema(TableName.MembershipRole),
|
||||
db.ref("temporaryRange").withSchema(TableName.MembershipRole),
|
||||
db.ref("temporaryAccessStartTime").withSchema(TableName.MembershipRole),
|
||||
db.ref("temporaryAccessEndTime").withSchema(TableName.MembershipRole),
|
||||
db.ref("name").as("projectName").withSchema(TableName.Project),
|
||||
db.ref("isActive").withSchema(TableName.OrgMembership)
|
||||
db.ref("isActive").withSchema("orgMembership")
|
||||
)
|
||||
.where({ isGhost: false })
|
||||
.orderBy(`${TableName.Users}.username` as "username");
|
||||
@@ -184,9 +169,11 @@ export const projectMembershipDALFactory = (db: TDbClient) => {
|
||||
|
||||
const findProjectGhostUser = async (projectId: string, tx?: Knex) => {
|
||||
try {
|
||||
const ghostUser = await (tx || db.replicaNode())(TableName.ProjectMembership)
|
||||
.where({ projectId })
|
||||
.join(TableName.Users, `${TableName.ProjectMembership}.userId`, `${TableName.Users}.id`)
|
||||
const ghostUser = await (tx || db.replicaNode())(TableName.Membership)
|
||||
.where({ [`${TableName.Membership}.scopeProjectId` as "scopeProjectId"]: projectId })
|
||||
.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))
|
||||
.where({ isGhost: true })
|
||||
.first();
|
||||
@@ -200,21 +187,24 @@ export const projectMembershipDALFactory = (db: TDbClient) => {
|
||||
const findMembershipsByUsername = async (projectId: string, usernames: string[]) => {
|
||||
try {
|
||||
const members = await db
|
||||
.replicaNode()(TableName.ProjectMembership)
|
||||
.where({ projectId })
|
||||
.join(TableName.Users, `${TableName.ProjectMembership}.userId`, `${TableName.Users}.id`)
|
||||
.replicaNode()(TableName.Membership)
|
||||
.where({ [`${TableName.Membership}.scopeProjectId` as "scopeProjectId"]: projectId })
|
||||
.where({ [`${TableName.Membership}.scope` as "scope"]: AccessScope.Project })
|
||||
.whereNotNull(`${TableName.Membership}.actorUserId`)
|
||||
.join(TableName.Users, `${TableName.Membership}.actorUserId`, `${TableName.Users}.id`)
|
||||
.join<TUserEncryptionKeys>(
|
||||
TableName.UserEncryptionKey,
|
||||
`${TableName.UserEncryptionKey}.userId`,
|
||||
`${TableName.Users}.id`
|
||||
)
|
||||
.select(
|
||||
selectAllTableCols(TableName.ProjectMembership),
|
||||
selectAllTableCols(TableName.Membership),
|
||||
db.ref("id").withSchema(TableName.Users).as("userId"),
|
||||
db.ref("username").withSchema(TableName.Users)
|
||||
)
|
||||
.whereIn("username", usernames)
|
||||
.where({ isGhost: false });
|
||||
|
||||
return members.map(({ userId, username, ...data }) => ({
|
||||
...data,
|
||||
user: { id: userId, username }
|
||||
@@ -227,9 +217,11 @@ export const projectMembershipDALFactory = (db: TDbClient) => {
|
||||
const findProjectMembershipsByUserId = async (orgId: string, userId: string) => {
|
||||
try {
|
||||
const docs = await db
|
||||
.replicaNode()(TableName.ProjectMembership)
|
||||
.join(TableName.Project, `${TableName.ProjectMembership}.projectId`, `${TableName.Project}.id`)
|
||||
.join(TableName.Users, `${TableName.ProjectMembership}.userId`, `${TableName.Users}.id`)
|
||||
.replicaNode()(TableName.Membership)
|
||||
.where({ [`${TableName.Membership}.scope` as "scope"]: AccessScope.Project })
|
||||
.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.Project}.orgId`, orgId)
|
||||
.join<TUserEncryptionKeys>(
|
||||
@@ -237,18 +229,10 @@ export const projectMembershipDALFactory = (db: TDbClient) => {
|
||||
`${TableName.UserEncryptionKey}.userId`,
|
||||
`${TableName.Users}.id`
|
||||
)
|
||||
.join(
|
||||
TableName.ProjectUserMembershipRole,
|
||||
`${TableName.ProjectUserMembershipRole}.projectMembershipId`,
|
||||
`${TableName.ProjectMembership}.id`
|
||||
)
|
||||
.leftJoin(
|
||||
TableName.ProjectRoles,
|
||||
`${TableName.ProjectUserMembershipRole}.customRoleId`,
|
||||
`${TableName.ProjectRoles}.id`
|
||||
)
|
||||
.join(TableName.MembershipRole, `${TableName.MembershipRole}.membershipId`, `${TableName.Membership}.id`)
|
||||
.leftJoin(TableName.Role, `${TableName.MembershipRole}.customRoleId`, `${TableName.Role}.id`)
|
||||
.select(
|
||||
db.ref("id").withSchema(TableName.ProjectMembership),
|
||||
db.ref("id").withSchema(TableName.Membership),
|
||||
db.ref("isGhost").withSchema(TableName.Users),
|
||||
db.ref("username").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("lastName").withSchema(TableName.Users),
|
||||
db.ref("id").withSchema(TableName.Users).as("userId"),
|
||||
db.ref("role").withSchema(TableName.ProjectUserMembershipRole),
|
||||
db.ref("id").withSchema(TableName.ProjectUserMembershipRole).as("membershipRoleId"),
|
||||
db.ref("customRoleId").withSchema(TableName.ProjectUserMembershipRole),
|
||||
db.ref("name").withSchema(TableName.ProjectRoles).as("customRoleName"),
|
||||
db.ref("slug").withSchema(TableName.ProjectRoles).as("customRoleSlug"),
|
||||
db.ref("temporaryMode").withSchema(TableName.ProjectUserMembershipRole),
|
||||
db.ref("isTemporary").withSchema(TableName.ProjectUserMembershipRole),
|
||||
db.ref("temporaryRange").withSchema(TableName.ProjectUserMembershipRole),
|
||||
db.ref("temporaryAccessStartTime").withSchema(TableName.ProjectUserMembershipRole),
|
||||
db.ref("temporaryAccessEndTime").withSchema(TableName.ProjectUserMembershipRole),
|
||||
db.ref("role").withSchema(TableName.MembershipRole),
|
||||
db.ref("id").withSchema(TableName.MembershipRole).as("membershipRoleId"),
|
||||
db.ref("customRoleId").withSchema(TableName.MembershipRole),
|
||||
db.ref("name").withSchema(TableName.Role).as("customRoleName"),
|
||||
db.ref("slug").withSchema(TableName.Role).as("customRoleSlug"),
|
||||
db.ref("temporaryMode").withSchema(TableName.MembershipRole),
|
||||
db.ref("isTemporary").withSchema(TableName.MembershipRole),
|
||||
db.ref("temporaryRange").withSchema(TableName.MembershipRole),
|
||||
db.ref("temporaryAccessStartTime").withSchema(TableName.MembershipRole),
|
||||
db.ref("temporaryAccessEndTime").withSchema(TableName.MembershipRole),
|
||||
db.ref("name").as("projectName").withSchema(TableName.Project),
|
||||
db.ref("id").as("projectId").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[]) => {
|
||||
try {
|
||||
const docs = await db
|
||||
.replicaNode()(TableName.ProjectMembership)
|
||||
.join(TableName.Project, `${TableName.ProjectMembership}.projectId`, `${TableName.Project}.id`)
|
||||
.join(TableName.Users, `${TableName.ProjectMembership}.userId`, `${TableName.Users}.id`)
|
||||
.replicaNode()(TableName.Membership)
|
||||
.where({ [`${TableName.Membership}.scope` as "scope"]: AccessScope.Project })
|
||||
.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)
|
||||
.where(`${TableName.Project}.orgId`, orgId)
|
||||
.join<TUserEncryptionKeys>(
|
||||
@@ -346,18 +332,10 @@ export const projectMembershipDALFactory = (db: TDbClient) => {
|
||||
`${TableName.UserEncryptionKey}.userId`,
|
||||
`${TableName.Users}.id`
|
||||
)
|
||||
.join(
|
||||
TableName.ProjectUserMembershipRole,
|
||||
`${TableName.ProjectUserMembershipRole}.projectMembershipId`,
|
||||
`${TableName.ProjectMembership}.id`
|
||||
)
|
||||
.leftJoin(
|
||||
TableName.ProjectRoles,
|
||||
`${TableName.ProjectUserMembershipRole}.customRoleId`,
|
||||
`${TableName.ProjectRoles}.id`
|
||||
)
|
||||
.join(TableName.MembershipRole, `${TableName.MembershipRole}.membershipId`, `${TableName.Membership}.id`)
|
||||
.leftJoin(TableName.Role, `${TableName.MembershipRole}.customRoleId`, `${TableName.Role}.id`)
|
||||
.select(
|
||||
db.ref("id").withSchema(TableName.ProjectMembership),
|
||||
db.ref("id").withSchema(TableName.Membership),
|
||||
db.ref("isGhost").withSchema(TableName.Users),
|
||||
db.ref("username").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("lastName").withSchema(TableName.Users),
|
||||
db.ref("id").withSchema(TableName.Users).as("userId"),
|
||||
db.ref("role").withSchema(TableName.ProjectUserMembershipRole),
|
||||
db.ref("id").withSchema(TableName.ProjectUserMembershipRole).as("membershipRoleId"),
|
||||
db.ref("customRoleId").withSchema(TableName.ProjectUserMembershipRole),
|
||||
db.ref("name").withSchema(TableName.ProjectRoles).as("customRoleName"),
|
||||
db.ref("slug").withSchema(TableName.ProjectRoles).as("customRoleSlug"),
|
||||
db.ref("temporaryMode").withSchema(TableName.ProjectUserMembershipRole),
|
||||
db.ref("isTemporary").withSchema(TableName.ProjectUserMembershipRole),
|
||||
db.ref("temporaryRange").withSchema(TableName.ProjectUserMembershipRole),
|
||||
db.ref("temporaryAccessStartTime").withSchema(TableName.ProjectUserMembershipRole),
|
||||
db.ref("temporaryAccessEndTime").withSchema(TableName.ProjectUserMembershipRole),
|
||||
db.ref("role").withSchema(TableName.MembershipRole),
|
||||
db.ref("id").withSchema(TableName.MembershipRole).as("membershipRoleId"),
|
||||
db.ref("customRoleId").withSchema(TableName.MembershipRole),
|
||||
db.ref("name").withSchema(TableName.Role).as("customRoleName"),
|
||||
db.ref("slug").withSchema(TableName.Role).as("customRoleSlug"),
|
||||
db.ref("temporaryMode").withSchema(TableName.MembershipRole),
|
||||
db.ref("isTemporary").withSchema(TableName.MembershipRole),
|
||||
db.ref("temporaryRange").withSchema(TableName.MembershipRole),
|
||||
db.ref("temporaryAccessStartTime").withSchema(TableName.MembershipRole),
|
||||
db.ref("temporaryAccessEndTime").withSchema(TableName.MembershipRole),
|
||||
db.ref("name").as("projectName").withSchema(TableName.Project),
|
||||
db.ref("id").as("projectId").withSchema(TableName.Project),
|
||||
db.ref("type").as("projectType").withSchema(TableName.Project)
|
||||
@@ -444,7 +422,6 @@ export const projectMembershipDALFactory = (db: TDbClient) => {
|
||||
};
|
||||
|
||||
return {
|
||||
...projectMemberOrm,
|
||||
findAllProjectMembers,
|
||||
findProjectGhostUser,
|
||||
findMembershipsByUsername,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* eslint-disable no-await-in-loop */
|
||||
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 {
|
||||
constructPermissionErrorMessage,
|
||||
@@ -41,21 +41,25 @@ import {
|
||||
TUpdateProjectMembershipDTO
|
||||
} from "./project-membership-types";
|
||||
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 = {
|
||||
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission" | "getProjectPermissionByRoles"| "invalidateProjectPermissionCache">;
|
||||
smtpService: TSmtpService;
|
||||
projectBotDAL: TProjectBotDALFactory;
|
||||
projectMembershipDAL: TProjectMembershipDALFactory;
|
||||
projectUserMembershipRoleDAL: Pick<TProjectUserMembershipRoleDALFactory, "insertMany" | "find" | "delete">;
|
||||
membershipDAL: TMembershipDALFactory;
|
||||
membershipRoleDAL: Pick<TMembershipRoleDALFactory, "insertMany" | "find" | "delete">;
|
||||
userDAL: Pick<TUserDALFactory, "findById" | "findOne" | "findUserByProjectMembershipId" | "find">;
|
||||
userGroupMembershipDAL: TUserGroupMembershipDALFactory;
|
||||
projectRoleDAL: Pick<TProjectRoleDALFactory, "find" | "findOne">;
|
||||
orgDAL: Pick<TOrgDALFactory, "findMembership" | "findOrgMembersByUsername">;
|
||||
roleDAL: Pick<TRoleDALFactory, "find" | "findOne">;
|
||||
orgDAL: Pick<TOrgDALFactory, "findMembership" | "findOrgMembersByUsername" | "findById">;
|
||||
projectDAL: Pick<TProjectDALFactory, "findById" | "findProjectGhostUser" | "transaction" | "findProjectById">;
|
||||
projectKeyDAL: Pick<TProjectKeyDALFactory, "findLatestProjectKey" | "delete" | "insertMany">;
|
||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||
projectUserAdditionalPrivilegeDAL: Pick<TProjectUserAdditionalPrivilegeDALFactory, "delete">;
|
||||
additionalPrivilegeDAL: Pick<TAdditionalPrivilegeDALFactory, "delete">;
|
||||
secretReminderRecipientsDAL: Pick<TSecretReminderRecipientsDALFactory, "delete">;
|
||||
groupProjectDAL: TGroupProjectDALFactory;
|
||||
notificationService: Pick<TNotificationServiceFactory, "createUserNotifications">;
|
||||
@@ -68,9 +72,7 @@ export const projectMembershipServiceFactory = ({
|
||||
projectMembershipDAL,
|
||||
projectUserMembershipRoleDAL,
|
||||
smtpService,
|
||||
projectRoleDAL,
|
||||
orgDAL,
|
||||
projectUserAdditionalPrivilegeDAL,
|
||||
userDAL,
|
||||
userGroupMembershipDAL,
|
||||
groupProjectDAL,
|
||||
@@ -78,7 +80,11 @@ export const projectMembershipServiceFactory = ({
|
||||
projectKeyDAL,
|
||||
secretReminderRecipientsDAL,
|
||||
licenseService,
|
||||
notificationService
|
||||
notificationService,
|
||||
roleDAL,
|
||||
membershipRoleDAL,
|
||||
additionalPrivilegeDAL,
|
||||
membershipDAL
|
||||
}: TProjectMembershipServiceFactoryDep) => {
|
||||
const getProjectMemberships = async ({
|
||||
actorId,
|
||||
@@ -170,6 +176,7 @@ export const projectMembershipServiceFactory = ({
|
||||
return membership;
|
||||
};
|
||||
|
||||
// TODO(simp): cross-check akhil
|
||||
const addUsersToProject = async ({
|
||||
projectId,
|
||||
actorId,
|
||||
@@ -191,17 +198,20 @@ export const projectMembershipServiceFactory = ({
|
||||
actionProjectType: ActionProjectType.Any
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionMemberActions.Create, ProjectPermissionSub.Member);
|
||||
const orgMembers = await orgDAL.findMembership({
|
||||
[`${TableName.OrgMembership}.orgId` as "orgId"]: project.orgId,
|
||||
const orgMembers = await membershipDAL.find({
|
||||
[`${TableName.Membership}.scopeOrgId` as "scopeOrgId"]: project.orgId,
|
||||
scope: AccessScope.Organization,
|
||||
$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" });
|
||||
|
||||
const existingMembers = await projectMembershipDAL.find({
|
||||
projectId,
|
||||
$in: { userId: orgMembers.map(({ userId }) => userId).filter(Boolean) }
|
||||
const existingMembers = await membershipDAL.find({
|
||||
[`${TableName.Membership}.scopeProjectId` as "scopeProjectId"]: projectId,
|
||||
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" });
|
||||
|
||||
@@ -212,7 +222,7 @@ export const projectMembershipServiceFactory = ({
|
||||
)
|
||||
);
|
||||
|
||||
await projectMembershipDAL.transaction(async (tx) => {
|
||||
await membershipDAL.transaction(async (tx) => {
|
||||
const projectMemberships = await projectMembershipDAL.insertMany(
|
||||
orgMembers.map(({ userId }) => ({
|
||||
projectId,
|
||||
@@ -275,7 +285,7 @@ export const projectMembershipServiceFactory = ({
|
||||
membershipId,
|
||||
roles
|
||||
}: TUpdateProjectMembershipDTO) => {
|
||||
const { permission, membership } = await permissionService.getProjectPermission({
|
||||
const { permission, memberships } = await permissionService.getProjectPermission({
|
||||
actor,
|
||||
actorId,
|
||||
projectId,
|
||||
@@ -290,14 +300,14 @@ export const projectMembershipServiceFactory = ({
|
||||
throw new ForbiddenRequestError({ message: "Forbidden member update" });
|
||||
}
|
||||
|
||||
for await (const { role: requestedRoleChange } of roles) {
|
||||
const { permission: rolePermission } = await permissionService.getProjectPermissionByRole(
|
||||
requestedRoleChange,
|
||||
projectId
|
||||
);
|
||||
|
||||
const providedRolePermissionDetails = await permissionService.getProjectPermissionByRoles(
|
||||
roles.map((el) => el.role).filter((el) => el !== ProjectMembershipRole.NoAccess),
|
||||
projectId
|
||||
);
|
||||
const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(actorOrgId);
|
||||
for await (const { permission: rolePermission } of providedRolePermissionDetails) {
|
||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||
membership.shouldUseNewPrivilegeSystem,
|
||||
shouldUseNewPrivilegeSystem,
|
||||
ProjectPermissionMemberActions.GrantPrivileges,
|
||||
ProjectPermissionSub.Member,
|
||||
permission,
|
||||
@@ -306,8 +316,8 @@ export const projectMembershipServiceFactory = ({
|
||||
if (!permissionBoundary.isValid)
|
||||
throw new PermissionBoundaryError({
|
||||
message: constructPermissionErrorMessage(
|
||||
`Failed to change role ${requestedRoleChange}`,
|
||||
membership.shouldUseNewPrivilegeSystem,
|
||||
"Failed to assign to role",
|
||||
shouldUseNewPrivilegeSystem,
|
||||
ProjectPermissionMemberActions.GrantPrivileges,
|
||||
ProjectPermissionSub.Member
|
||||
),
|
||||
@@ -322,7 +332,7 @@ export const projectMembershipServiceFactory = ({
|
||||
// we don't want to include custom in this check;
|
||||
// this unintentionally enables setting slug to custom which is reserved
|
||||
.filter((r) => r !== ProjectMembershipRole.Custom)
|
||||
.includes(role as ProjectMembershipRole)
|
||||
.includes(role as ProjectMembershipRole.Member)
|
||||
);
|
||||
const hasCustomRole = Boolean(customInputRoles.length);
|
||||
if (hasCustomRole) {
|
||||
@@ -334,7 +344,7 @@ export const projectMembershipServiceFactory = ({
|
||||
}
|
||||
|
||||
const customRoles = hasCustomRole
|
||||
? await projectRoleDAL.find({
|
||||
? await roleDAL.find({
|
||||
projectId,
|
||||
$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);
|
||||
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);
|
||||
@@ -2,6 +2,7 @@ import { Knex } from "knex";
|
||||
|
||||
import { TDbClient } from "@app/db";
|
||||
import {
|
||||
AccessScope,
|
||||
ProjectsSchema,
|
||||
ProjectType,
|
||||
ProjectUpgradeStatus,
|
||||
@@ -24,10 +25,13 @@ export const projectDALFactory = (db: TDbClient) => {
|
||||
|
||||
const findIdentityProjects = async (identityId: string, orgId: string, projectType?: ProjectType) => {
|
||||
try {
|
||||
const workspaces = await db(TableName.IdentityProjectMembership)
|
||||
.where({ identityId })
|
||||
.join(TableName.Project, `${TableName.IdentityProjectMembership}.projectId`, `${TableName.Project}.id`)
|
||||
const workspaces = await db
|
||||
.replicaNode()(TableName.Membership)
|
||||
.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)
|
||||
.join(TableName.Project, `${TableName.Membership}.scopeProjectId`, `${TableName.Project}.id`)
|
||||
.andWhere((qb) => {
|
||||
if (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) => {
|
||||
try {
|
||||
const workspaces = await db
|
||||
.replicaNode()(TableName.ProjectMembership)
|
||||
.where({ userId })
|
||||
.join(TableName.Project, `${TableName.ProjectMembership}.projectId`, `${TableName.Project}.id`)
|
||||
const userGroupSubquery = db
|
||||
.replicaNode()(TableName.Groups)
|
||||
.leftJoin(TableName.UserGroupMembership, `${TableName.UserGroupMembership}.groupId`, `${TableName.Groups}.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)
|
||||
.andWhere((qb) => {
|
||||
void qb
|
||||
.where(`${TableName.Membership}.actorUserId`, userId)
|
||||
.orWhereIn(`${TableName.Membership}.actorGroupId`, userGroupSubquery);
|
||||
})
|
||||
.andWhere((qb) => {
|
||||
if (projectType) {
|
||||
void qb.where(`${TableName.Project}.type`, projectType);
|
||||
@@ -97,36 +113,8 @@ export const projectDALFactory = (db: TDbClient) => {
|
||||
{ column: `${TableName.Environment}.position`, order: "asc" }
|
||||
]);
|
||||
|
||||
const groups = db(TableName.UserGroupMembership).where({ userId }).select("groupId");
|
||||
|
||||
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),
|
||||
const formattedProjects = sqlNestRelationships({
|
||||
data: projects,
|
||||
key: "id",
|
||||
parentMapper: ({ _id, ...el }) => ({ _id, ...ProjectsSchema.parse(el) }),
|
||||
childrenMapper: [
|
||||
@@ -142,7 +130,7 @@ export const projectDALFactory = (db: TDbClient) => {
|
||||
]
|
||||
});
|
||||
|
||||
return nestedWorkspaces.map((workspace) => ({
|
||||
return formattedProjects.map((workspace) => ({
|
||||
...workspace,
|
||||
organization: workspace.orgId
|
||||
}));
|
||||
@@ -153,9 +141,10 @@ export const projectDALFactory = (db: TDbClient) => {
|
||||
|
||||
const findProjectGhostUser = async (projectId: string, tx?: Knex) => {
|
||||
try {
|
||||
const ghostUser = await (tx || db.replicaNode())(TableName.ProjectMembership)
|
||||
.where({ projectId })
|
||||
.join(TableName.Users, `${TableName.ProjectMembership}.userId`, `${TableName.Users}.id`)
|
||||
const ghostUser = await (tx || db.replicaNode())(TableName.Membership)
|
||||
.where(`${TableName.Membership}.scope`, AccessScope.Project)
|
||||
.where(`${TableName.Membership}.scopeProjectId`, projectId)
|
||||
.join(TableName.Users, `${TableName.Membership}.actorUserId`, `${TableName.Users}.id`)
|
||||
.select(selectAllTableCols(TableName.Users))
|
||||
.where({ isGhost: true })
|
||||
.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) => {
|
||||
try {
|
||||
const workspaces = await db
|
||||
@@ -402,18 +343,27 @@ export const projectDALFactory = (db: TDbClient) => {
|
||||
projectIds?: string[];
|
||||
}) => {
|
||||
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 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");
|
||||
const identityMembershipSubQuery = db(TableName.Membership).where({ identityId: dto.actorId }).select("projectId");
|
||||
|
||||
// Get the SQL strings for the subqueries
|
||||
const userMembershipSql = userMembershipSubquery.toQuery();
|
||||
const groupMembershipSql = groupMembershipSubquery.toQuery();
|
||||
const identityMembershipSql = identityMembershipSubQuery.toQuery();
|
||||
|
||||
const query = db
|
||||
@@ -431,7 +381,7 @@ export const projectDALFactory = (db: TDbClient) => {
|
||||
ELSE FALSE
|
||||
END as "isMember"
|
||||
`,
|
||||
[db.raw(userMembershipSql), db.raw(groupMembershipSql)]
|
||||
[db.raw(userMembershipSql)]
|
||||
)
|
||||
: db.raw(
|
||||
`
|
||||
@@ -495,7 +445,6 @@ export const projectDALFactory = (db: TDbClient) => {
|
||||
findUserProjects,
|
||||
findIdentityProjects,
|
||||
setProjectUpgradeStatus,
|
||||
findAllProjectsByIdentity,
|
||||
findProjectGhostUser,
|
||||
findProjectById,
|
||||
findProjectByFilter,
|
||||
|
||||
@@ -29,6 +29,10 @@ export const newNamespaceRoleFactory = (_dto: TNamespaceRoleScopeFactoryDep): TR
|
||||
throw new BadRequestError({ message: "Invalid scope provided for the factory" });
|
||||
};
|
||||
|
||||
const isCustomRole: TRoleScopeFactory["isCustomRole"] = () => false;
|
||||
|
||||
const getPredefinedRoles: TRoleScopeFactory["getPredefinedRoles"] = async () => [];
|
||||
|
||||
return {
|
||||
onCreateRoleGuard,
|
||||
onUpdateRoleGuard,
|
||||
@@ -36,6 +40,8 @@ export const newNamespaceRoleFactory = (_dto: TNamespaceRoleScopeFactoryDep): TR
|
||||
onListRoleGuard,
|
||||
onGetRoleByIdGuard,
|
||||
onGetRoleBySlugGuard,
|
||||
getScopeField
|
||||
getScopeField,
|
||||
isCustomRole,
|
||||
getPredefinedRoles
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,11 +1,18 @@
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
|
||||
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 { BadRequestError } from "@app/lib/errors";
|
||||
|
||||
import { TRoleScopeFactory } from "../role-types";
|
||||
import { isCustomOrgRole } from "@app/services/org/org-role-fns";
|
||||
|
||||
type TOrgRoleScopeFactoryDep = {
|
||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
||||
@@ -19,6 +26,8 @@ export const newOrgRoleFactory = ({ permissionService }: TOrgRoleScopeFactoryDep
|
||||
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 { permission } = await permissionService.getOrgPermission(
|
||||
dto.permission.type,
|
||||
@@ -85,6 +94,42 @@ export const newOrgRoleFactory = ({ permissionService }: TOrgRoleScopeFactoryDep
|
||||
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 {
|
||||
onCreateRoleGuard,
|
||||
onUpdateRoleGuard,
|
||||
@@ -92,6 +137,8 @@ export const newOrgRoleFactory = ({ permissionService }: TOrgRoleScopeFactoryDep
|
||||
onListRoleGuard,
|
||||
onGetRoleByIdGuard,
|
||||
onGetRoleBySlugGuard,
|
||||
getScopeField
|
||||
getScopeField,
|
||||
getPredefinedRoles,
|
||||
isCustomRole
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,17 +1,35 @@
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
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 { 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 { 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 = {
|
||||
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) => {
|
||||
if (dto.scope === AccessScope.Project) {
|
||||
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" });
|
||||
};
|
||||
|
||||
const isCustomRole: TRoleScopeFactory["isCustomRole"] = (role: string) => isCustomProjectRole(role);
|
||||
|
||||
const onCreateRoleGuard: TRoleScopeFactory["onCreateRoleGuard"] = async (dto) => {
|
||||
const scope = getScopeField(dto.scopeData);
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
@@ -97,6 +117,78 @@ export const newProjectRoleFactory = ({ permissionService }: TProjectRoleScopeFa
|
||||
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 {
|
||||
onCreateRoleGuard,
|
||||
onUpdateRoleGuard,
|
||||
@@ -104,6 +196,8 @@ export const newProjectRoleFactory = ({ permissionService }: TProjectRoleScopeFa
|
||||
onListRoleGuard,
|
||||
onGetRoleByIdGuard,
|
||||
onGetRoleBySlugGuard,
|
||||
getScopeField
|
||||
getScopeField,
|
||||
getPredefinedRoles,
|
||||
isCustomRole
|
||||
};
|
||||
};
|
||||
|
||||
@@ -16,20 +16,23 @@ import {
|
||||
TListRoleDTO,
|
||||
TUpdateRoleDTO
|
||||
} from "./role-types";
|
||||
import { TProjectDALFactory } from "../project/project-dal";
|
||||
|
||||
type TRoleServiceFactoryDep = {
|
||||
roleDAL: TRoleDALFactory;
|
||||
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission" | "getOrgPermission">;
|
||||
projectDAL: Pick<TProjectDALFactory, "findById">;
|
||||
};
|
||||
|
||||
export type TRoleServiceFactory = ReturnType<typeof roleServiceFactory>;
|
||||
|
||||
export const roleServiceFactory = ({ roleDAL, permissionService }: TRoleServiceFactoryDep) => {
|
||||
export const roleServiceFactory = ({ roleDAL, permissionService, projectDAL }: TRoleServiceFactoryDep) => {
|
||||
const orgRoleFactory = newOrgRoleFactory({
|
||||
permissionService
|
||||
});
|
||||
const projectRoleFactory = newProjectRoleFactory({
|
||||
permissionService
|
||||
permissionService,
|
||||
projectDAL
|
||||
});
|
||||
const namespaceRoleFactory = newNamespaceRoleFactory({
|
||||
permissionService
|
||||
@@ -130,6 +133,7 @@ export const roleServiceFactory = ({ roleDAL, permissionService }: TRoleServiceF
|
||||
await factory.onListRoleGuard(dto);
|
||||
|
||||
const scope = factory.getScopeField(scopeData);
|
||||
const predefinedRoles = await factory.getPredefinedRoles(scopeData);
|
||||
const roles = await roleDAL.find(
|
||||
{
|
||||
[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"]] }
|
||||
);
|
||||
|
||||
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) => {
|
||||
@@ -163,6 +169,14 @@ export const roleServiceFactory = ({ roleDAL, permissionService }: TRoleServiceF
|
||||
await factory.onGetRoleBySlugGuard(dto);
|
||||
|
||||
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({
|
||||
slug: selector.slug,
|
||||
[scope.key]: scope.value
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
import { AccessScopeData } from "@app/db/schemas";
|
||||
import { AccessScopeData, TRoles } from "@app/db/schemas";
|
||||
import { OrgServiceActor } from "@app/lib/types";
|
||||
import { MongoAbility, RawRuleOf } from "@casl/ability";
|
||||
|
||||
export interface TRoleScopeFactory {
|
||||
onCreateRoleGuard: (arg: TCreateRoleDTO) => Promise<void>;
|
||||
onUpdateRoleGuard: (arg: TUpdateRoleDTO) => Promise<void>;
|
||||
onDeleteRoleGuard: (arg: TDeleteRoleDTO) => Promise<void>;
|
||||
onListRoleGuard: (arg: TListRoleDTO) => Promise<void>;
|
||||
getPredefinedRoles: (arg: AccessScopeData) => Promise<(TRoles & { permissions: RawRuleOf<MongoAbility>[] })[]>;
|
||||
onGetRoleByIdGuard: (arg: TGetRoleByIdDTO) => Promise<void>;
|
||||
onGetRoleBySlugGuard: (arg: TGetRoleBySlugDTO) => Promise<void>;
|
||||
getScopeField: (scope: AccessScopeData) => { key: "orgId" | "namespaceId" | "projectId"; value: string };
|
||||
isCustomRole: (role: string) => boolean;
|
||||
}
|
||||
|
||||
export type TCreateRoleDTO = {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
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";
|
||||
|
||||
export type TSecretReminderRecipientsDALFactory = ReturnType<typeof secretReminderRecipientsDALFactory>;
|
||||
@@ -14,13 +14,13 @@ export const secretReminderRecipientsDALFactory = (db: TDbClient) => {
|
||||
.where({ secretId })
|
||||
.leftJoin(TableName.Users, `${TableName.SecretReminderRecipients}.userId`, `${TableName.Users}.id`)
|
||||
.leftJoin(TableName.Project, `${TableName.SecretReminderRecipients}.projectId`, `${TableName.Project}.id`)
|
||||
.leftJoin(TableName.OrgMembership, (bd) => {
|
||||
.leftJoin(TableName.Membership, (bd) => {
|
||||
void bd
|
||||
.on(`${TableName.OrgMembership}.userId`, "=", `${TableName.SecretReminderRecipients}.userId`)
|
||||
.andOn(`${TableName.OrgMembership}.orgId`, "=", `${TableName.Project}.orgId`);
|
||||
.on(`${TableName.Membership}.actorUserId`, "=", `${TableName.SecretReminderRecipients}.userId`)
|
||||
.andOn(`${TableName.Membership}.scopeOrgId`, "=", `${TableName.Project}.orgId`)
|
||||
.andOn(`${TableName.Membership}.scope`, db.raw("?", [AccessScope.Organization]));
|
||||
})
|
||||
|
||||
.where(`${TableName.OrgMembership}.isActive`, true)
|
||||
.where(`${TableName.Membership}.isActive`, true)
|
||||
.select(selectAllTableCols(TableName.SecretReminderRecipients))
|
||||
.select(
|
||||
db.ref("email").withSchema(TableName.Users).as("email"),
|
||||
|
||||
@@ -2,7 +2,13 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
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 { buildFindFilter, ormify, selectAllTableCols, sqlNestRelationships, TFindOpt } from "@app/lib/knex";
|
||||
import { logger } from "@app/lib/logger";
|
||||
@@ -191,11 +197,11 @@ export const secretVersionV2BridgeDALFactory = (db: TDbClient) => {
|
||||
const { offset, limit, sort = [["createdAt", "desc"]] } = findOpt;
|
||||
const query = (tx || db.replicaNode())(TableName.SecretVersionV2)
|
||||
.leftJoin(TableName.Users, `${TableName.Users}.id`, `${TableName.SecretVersionV2}.userActorId`)
|
||||
.leftJoin(
|
||||
TableName.ProjectMembership,
|
||||
`${TableName.ProjectMembership}.userId`,
|
||||
`${TableName.SecretVersionV2}.userActorId`
|
||||
)
|
||||
.leftJoin(TableName.Membership, (qb) => {
|
||||
void qb
|
||||
.on(`${TableName.Membership}.actorUserId`, `${TableName.SecretVersionV2}.userActorId`)
|
||||
.andOn(`${TableName.Membership}.scope`, db.raw("?", [AccessScope.Project]));
|
||||
})
|
||||
.leftJoin(TableName.Identity, `${TableName.Identity}.id`, `${TableName.SecretVersionV2}.identityActorId`)
|
||||
.leftJoin(TableName.SecretV2, `${TableName.SecretVersionV2}.secretId`, `${TableName.SecretV2}.id`)
|
||||
.leftJoin(
|
||||
@@ -210,19 +216,19 @@ export const secretVersionV2BridgeDALFactory = (db: TDbClient) => {
|
||||
)
|
||||
.where((qb) => {
|
||||
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);
|
||||
})
|
||||
.orWhere((qb) => {
|
||||
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);
|
||||
})
|
||||
.select(
|
||||
selectAllTableCols(TableName.SecretVersionV2),
|
||||
db.ref("username").withSchema(TableName.Users).as("userActorName"),
|
||||
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("color").withSchema(TableName.SecretTag).as("tagColor"),
|
||||
db.ref("slug").withSchema(TableName.SecretTag).as("tagSlug")
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Knex } from "knex";
|
||||
|
||||
import { TDbClient } from "@app/db";
|
||||
import {
|
||||
AccessScope,
|
||||
TableName,
|
||||
TUserActionsInsert,
|
||||
TUserActionsUpdate,
|
||||
@@ -127,9 +128,13 @@ export const userDALFactory = (db: TDbClient) => {
|
||||
const findUserByProjectMembershipId = async (projectMembershipId: string) => {
|
||||
try {
|
||||
return await db
|
||||
.replicaNode()(TableName.ProjectMembership)
|
||||
.where({ [`${TableName.ProjectMembership}.id` as "id"]: projectMembershipId })
|
||||
.join(TableName.Users, `${TableName.ProjectMembership}.userId`, `${TableName.Users}.id`)
|
||||
.replicaNode()(TableName.Membership)
|
||||
.where({
|
||||
[`${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();
|
||||
} catch (error) {
|
||||
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[]) => {
|
||||
try {
|
||||
return await db
|
||||
.replicaNode()(TableName.ProjectMembership)
|
||||
.whereIn(`${TableName.ProjectMembership}.id`, projectMembershipIds)
|
||||
.join(TableName.Users, `${TableName.ProjectMembership}.userId`, `${TableName.Users}.id`)
|
||||
.replicaNode()(TableName.Membership)
|
||||
.whereIn(`${TableName.Membership}.id`, projectMembershipIds)
|
||||
.where(`${TableName.Membership}.scope`, AccessScope.Project)
|
||||
.whereNotNull(`${TableName.Membership}.actorUserId`)
|
||||
.join(TableName.Users, `${TableName.Membership}.actorUserId`, `${TableName.Users}.id`)
|
||||
.select("*");
|
||||
} catch (error) {
|
||||
throw new DatabaseError({ error, name: "Find users by project membership ids" });
|
||||
@@ -191,8 +198,10 @@ export const userDALFactory = (db: TDbClient) => {
|
||||
try {
|
||||
const doc = await db(TableName.Users)
|
||||
.where({ email })
|
||||
.leftJoin(TableName.OrgMembership, `${TableName.OrgMembership}.userId`, `${TableName.Users}.id`)
|
||||
.leftJoin(TableName.Organization, `${TableName.Organization}.id`, `${TableName.OrgMembership}.orgId`)
|
||||
.leftJoin(TableName.Membership, `${TableName.Membership}.actorUserId`, `${TableName.Users}.id`)
|
||||
.where(`${TableName.Membership}.scope`, AccessScope.Organization)
|
||||
.whereNotNull(`${TableName.Membership}.actorUserId`)
|
||||
.leftJoin(TableName.Organization, `${TableName.Organization}.id`, `${TableName.Membership}.scopeOrgId`)
|
||||
.select(selectAllTableCols(TableName.Users))
|
||||
.select(
|
||||
db.ref("name").withSchema(TableName.Organization).as("orgName"),
|
||||
|
||||
Reference in New Issue
Block a user