feat: switched up all dal except policy ones

This commit is contained in:
=
2025-10-01 14:53:35 +05:30
parent 4d68d48d89
commit 0ddf98ec15
37 changed files with 1022 additions and 895 deletions

View File

@@ -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");

View File

@@ -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
}))
};
}
});
};

View File

@@ -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";

View File

@@ -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`,

View File

@@ -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();

View File

@@ -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")
);

View File

@@ -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();

View File

@@ -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;
};

View File

@@ -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);

View File

@@ -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 })) };
}
});

View File

@@ -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",

View File

@@ -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 };
};

View File

@@ -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;
};

View File

@@ -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

View File

@@ -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;
};

View File

@@ -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 };
};

View File

@@ -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 };

View File

@@ -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,

View File

@@ -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,

View File

@@ -1,7 +0,0 @@
import { TDbClient } from "@app/db";
import { TableName } from "@app/db/schemas";
import { ormify } from "@app/lib/knex";
export type TOrgRoleDALFactory = ReturnType<typeof orgRoleDALFactory>;
export const orgRoleDALFactory = (db: TDbClient) => ormify(db, TableName.OrgRoles);

View File

@@ -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",

View File

@@ -14,7 +14,7 @@ import { BadRequestError, NotFoundError } from "@app/lib/errors";
import { TExternalGroupOrgRoleMappingDALFactory } from "@app/services/external-group-org-role-mapping/external-group-org-role-mapping-dal";
import { 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 };

View File

@@ -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

View File

@@ -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`,

View File

@@ -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;

View File

@@ -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,

View File

@@ -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);
});

View File

@@ -1,7 +0,0 @@
import { TDbClient } from "@app/db";
import { TableName } from "@app/db/schemas";
import { ormify } from "@app/lib/knex";
export type TProjectRoleDALFactory = ReturnType<typeof projectRoleDALFactory>;
export const projectRoleDALFactory = (db: TDbClient) => ormify(db, TableName.ProjectRoles);

View File

@@ -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,

View File

@@ -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
};
};

View File

@@ -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
};
};

View File

@@ -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
};
};

View File

@@ -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

View File

@@ -1,14 +1,17 @@
import { AccessScopeData } from "@app/db/schemas";
import { AccessScopeData, TRoles } from "@app/db/schemas";
import { OrgServiceActor } from "@app/lib/types";
import { 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 = {

View File

@@ -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"),

View File

@@ -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")

View File

@@ -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"),