mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-15 12:38:36 +00:00
feature: project and org identity pagination, search and sort
This commit is contained in:
@@ -4,6 +4,7 @@ import { TDbClient } from "@app/db";
|
||||
import { TableName } from "@app/db/schemas";
|
||||
import { DatabaseError } from "@app/lib/errors";
|
||||
import { ormify, sqlNestRelationships } from "@app/lib/knex";
|
||||
import { TListProjectIdentityDTO } from "@app/services/identity-project/identity-project-types";
|
||||
|
||||
export type TIdentityProjectDALFactory = ReturnType<typeof identityProjectDALFactory>;
|
||||
|
||||
@@ -107,9 +108,16 @@ export const identityProjectDALFactory = (db: TDbClient) => {
|
||||
}
|
||||
};
|
||||
|
||||
const findByProjectId = async (projectId: string, filter: { identityId?: string } = {}, tx?: Knex) => {
|
||||
const findByProjectId = async (
|
||||
projectId: string,
|
||||
filter: { identityId?: string } & Pick<
|
||||
TListProjectIdentityDTO,
|
||||
"limit" | "offset" | "textFilter" | "orderBy" | "direction"
|
||||
> = {},
|
||||
tx?: Knex
|
||||
) => {
|
||||
try {
|
||||
const docs = await (tx || db.replicaNode())(TableName.IdentityProjectMembership)
|
||||
const query = (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`)
|
||||
@@ -117,6 +125,10 @@ export const identityProjectDALFactory = (db: TDbClient) => {
|
||||
if (filter.identityId) {
|
||||
void qb.where("identityId", filter.identityId);
|
||||
}
|
||||
|
||||
if (filter.textFilter) {
|
||||
void qb.whereILike(`${TableName.Identity}.name`, `%${filter.textFilter}%`);
|
||||
}
|
||||
})
|
||||
.join(
|
||||
TableName.IdentityProjectMembershipRole,
|
||||
@@ -154,6 +166,22 @@ export const identityProjectDALFactory = (db: TDbClient) => {
|
||||
db.ref("name").as("projectName").withSchema(TableName.Project)
|
||||
);
|
||||
|
||||
if (filter.limit) {
|
||||
void query.offset(filter.offset ?? 0).limit(filter.limit);
|
||||
}
|
||||
|
||||
if (filter.orderBy) {
|
||||
switch (filter.orderBy) {
|
||||
case "name":
|
||||
void query.orderBy(`${TableName.Identity}.${filter.orderBy}`, filter.direction);
|
||||
break;
|
||||
default:
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
|
||||
const docs = await query;
|
||||
|
||||
const members = sqlNestRelationships({
|
||||
data: docs,
|
||||
parentMapper: ({ identityId, identityName, identityAuthMethod, id, createdAt, updatedAt, projectName }) => ({
|
||||
@@ -208,9 +236,36 @@ export const identityProjectDALFactory = (db: TDbClient) => {
|
||||
}
|
||||
};
|
||||
|
||||
const getCountByProjectId = async (
|
||||
projectId: string,
|
||||
filter: { identityId?: string } & Pick<TListProjectIdentityDTO, "textFilter"> = {},
|
||||
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`)
|
||||
.where((qb) => {
|
||||
if (filter.identityId) {
|
||||
void qb.where("identityId", filter.identityId);
|
||||
}
|
||||
|
||||
if (filter.textFilter) {
|
||||
void qb.whereILike(`${TableName.Identity}.name`, `%${filter.textFilter}%`);
|
||||
}
|
||||
});
|
||||
|
||||
return identities.length;
|
||||
} catch (error) {
|
||||
throw new DatabaseError({ error, name: "GetCountByProjectId" });
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
...identityProjectOrm,
|
||||
findByIdentityId,
|
||||
findByProjectId
|
||||
findByProjectId,
|
||||
getCountByProjectId
|
||||
};
|
||||
};
|
||||
|
||||
@@ -268,7 +268,12 @@ export const identityProjectServiceFactory = ({
|
||||
actor,
|
||||
actorId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
actorOrgId,
|
||||
limit,
|
||||
offset,
|
||||
orderBy,
|
||||
direction,
|
||||
textFilter
|
||||
}: TListProjectIdentityDTO) => {
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
@@ -279,8 +284,17 @@ export const identityProjectServiceFactory = ({
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Identity);
|
||||
|
||||
const identityMemberships = await identityProjectDAL.findByProjectId(projectId);
|
||||
return identityMemberships;
|
||||
const identityMemberships = await identityProjectDAL.findByProjectId(projectId, {
|
||||
limit,
|
||||
offset,
|
||||
orderBy,
|
||||
direction,
|
||||
textFilter
|
||||
});
|
||||
|
||||
const totalCount = await identityProjectDAL.getCountByProjectId(projectId, { textFilter });
|
||||
|
||||
return { identityMemberships, totalCount };
|
||||
};
|
||||
|
||||
const getProjectIdentityByIdentityId = async ({
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { TProjectPermission } from "@app/lib/types";
|
||||
import { OrderByDirection, TProjectPermission } from "@app/lib/types";
|
||||
|
||||
import { ProjectUserMembershipTemporaryMode } from "../project-membership/project-membership-types";
|
||||
|
||||
@@ -40,8 +40,18 @@ export type TDeleteProjectIdentityDTO = {
|
||||
identityId: string;
|
||||
} & TProjectPermission;
|
||||
|
||||
export type TListProjectIdentityDTO = TProjectPermission;
|
||||
export type TListProjectIdentityDTO = {
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
orderBy?: ProjectIdentityOrderBy;
|
||||
direction?: OrderByDirection;
|
||||
textFilter?: string;
|
||||
} & TProjectPermission;
|
||||
|
||||
export type TGetProjectIdentityByIdentityIdDTO = {
|
||||
identityId: string;
|
||||
} & TProjectPermission;
|
||||
|
||||
export enum ProjectIdentityOrderBy {
|
||||
Name = "name"
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ import { TDbClient } from "@app/db";
|
||||
import { TableName, TIdentityOrgMemberships } from "@app/db/schemas";
|
||||
import { DatabaseError } from "@app/lib/errors";
|
||||
import { ormify, selectAllTableCols } from "@app/lib/knex";
|
||||
import { OrderByDirection } from "@app/lib/types";
|
||||
import { TListOrgIdentitiesByOrgIdDTO } from "@app/services/identity/identity-types";
|
||||
|
||||
export type TIdentityOrgDALFactory = ReturnType<typeof identityOrgDALFactory>;
|
||||
|
||||
@@ -27,9 +29,20 @@ export const identityOrgDALFactory = (db: TDbClient) => {
|
||||
}
|
||||
};
|
||||
|
||||
const find = async (filter: Partial<TIdentityOrgMemberships>, tx?: Knex) => {
|
||||
const find = async (
|
||||
{
|
||||
limit,
|
||||
offset = 0,
|
||||
orderBy,
|
||||
direction = OrderByDirection.ASC,
|
||||
textFilter,
|
||||
...filter
|
||||
}: Partial<TIdentityOrgMemberships> &
|
||||
Pick<TListOrgIdentitiesByOrgIdDTO, "offset" | "limit" | "orderBy" | "direction" | "textFilter">,
|
||||
tx?: Knex
|
||||
) => {
|
||||
try {
|
||||
const docs = await (tx || db.replicaNode())(TableName.IdentityOrgMembership)
|
||||
const query = (tx || db.replicaNode())(TableName.IdentityOrgMembership)
|
||||
.where(filter)
|
||||
.join(TableName.Identity, `${TableName.IdentityOrgMembership}.identityId`, `${TableName.Identity}.id`)
|
||||
.leftJoin(TableName.OrgRoles, `${TableName.IdentityOrgMembership}.roleId`, `${TableName.OrgRoles}.id`)
|
||||
@@ -44,6 +57,30 @@ export const identityOrgDALFactory = (db: TDbClient) => {
|
||||
.select(db.ref("id").as("identityId").withSchema(TableName.Identity))
|
||||
.select(db.ref("name").as("identityName").withSchema(TableName.Identity))
|
||||
.select(db.ref("authMethod").as("identityAuthMethod").withSchema(TableName.Identity));
|
||||
|
||||
if (limit) {
|
||||
void query.offset(offset).limit(limit);
|
||||
}
|
||||
|
||||
if (orderBy) {
|
||||
switch (orderBy) {
|
||||
case "name":
|
||||
void query.orderBy(`${TableName.Identity}.${orderBy}`, direction);
|
||||
break;
|
||||
case "role":
|
||||
void query.orderBy(`${TableName.IdentityOrgMembership}.${orderBy}`, direction);
|
||||
break;
|
||||
default:
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
|
||||
if (textFilter?.length) {
|
||||
void query.whereILike(`${TableName.Identity}.name`, `%${textFilter}%`);
|
||||
}
|
||||
|
||||
const docs = await query;
|
||||
|
||||
return docs.map(
|
||||
({
|
||||
crId,
|
||||
@@ -79,5 +116,26 @@ export const identityOrgDALFactory = (db: TDbClient) => {
|
||||
}
|
||||
};
|
||||
|
||||
return { ...identityOrgOrm, find, findOne };
|
||||
const countAllOrgIdentities = async (
|
||||
{ textFilter, ...filter }: Partial<TIdentityOrgMemberships> & Pick<TListOrgIdentitiesByOrgIdDTO, "textFilter">,
|
||||
tx?: Knex
|
||||
) => {
|
||||
try {
|
||||
const query = (tx || db.replicaNode())(TableName.IdentityOrgMembership)
|
||||
.where(filter)
|
||||
.join(TableName.Identity, `${TableName.IdentityOrgMembership}.identityId`, `${TableName.Identity}.id`);
|
||||
|
||||
if (textFilter?.length) {
|
||||
void query.whereILike(`${TableName.Identity}.name`, `%${textFilter}%`);
|
||||
}
|
||||
|
||||
const identities = await query;
|
||||
|
||||
return identities.length;
|
||||
} catch (error) {
|
||||
throw new DatabaseError({ error, name: "countAllOrgIdentities" });
|
||||
}
|
||||
};
|
||||
|
||||
return { ...identityOrgOrm, find, findOne, countAllOrgIdentities };
|
||||
};
|
||||
|
||||
@@ -6,7 +6,6 @@ import { OrgPermissionActions, OrgPermissionSubjects } from "@app/ee/services/pe
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||
import { isAtLeastAsPrivileged } from "@app/lib/casl";
|
||||
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
||||
import { TOrgPermission } from "@app/lib/types";
|
||||
import { TIdentityProjectDALFactory } from "@app/services/identity-project/identity-project-dal";
|
||||
|
||||
import { ActorType } from "../auth/auth-type";
|
||||
@@ -16,6 +15,7 @@ import {
|
||||
TCreateIdentityDTO,
|
||||
TDeleteIdentityDTO,
|
||||
TGetIdentityByIdDTO,
|
||||
TListOrgIdentitiesByOrgIdDTO,
|
||||
TListProjectIdentitiesByIdentityIdDTO,
|
||||
TUpdateIdentityDTO
|
||||
} from "./identity-types";
|
||||
@@ -195,14 +195,36 @@ export const identityServiceFactory = ({
|
||||
return { ...deletedIdentity, orgId: identityOrgMembership.orgId };
|
||||
};
|
||||
|
||||
const listOrgIdentities = async ({ orgId, actor, actorId, actorAuthMethod, actorOrgId }: TOrgPermission) => {
|
||||
const listOrgIdentities = async ({
|
||||
orgId,
|
||||
actor,
|
||||
actorId,
|
||||
actorAuthMethod,
|
||||
actorOrgId,
|
||||
limit,
|
||||
offset,
|
||||
orderBy,
|
||||
direction,
|
||||
textFilter
|
||||
}: TListOrgIdentitiesByOrgIdDTO) => {
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Identity);
|
||||
|
||||
const identityMemberships = await identityOrgMembershipDAL.find({
|
||||
[`${TableName.IdentityOrgMembership}.orgId` as "orgId"]: orgId
|
||||
[`${TableName.IdentityOrgMembership}.orgId` as "orgId"]: orgId,
|
||||
limit,
|
||||
offset,
|
||||
orderBy,
|
||||
direction,
|
||||
textFilter
|
||||
});
|
||||
return identityMemberships;
|
||||
|
||||
const totalCount = await identityOrgMembershipDAL.countAllOrgIdentities({
|
||||
[`${TableName.IdentityOrgMembership}.orgId` as "orgId"]: orgId,
|
||||
textFilter
|
||||
});
|
||||
|
||||
return { identityMemberships, totalCount };
|
||||
};
|
||||
|
||||
const listProjectIdentitiesByIdentityId = async ({
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { IPType } from "@app/lib/ip";
|
||||
import { TOrgPermission } from "@app/lib/types";
|
||||
import { OrderByDirection, TOrgPermission } from "@app/lib/types";
|
||||
|
||||
export type TCreateIdentityDTO = {
|
||||
role: string;
|
||||
@@ -29,3 +29,16 @@ export interface TIdentityTrustedIp {
|
||||
export type TListProjectIdentitiesByIdentityIdDTO = {
|
||||
identityId: string;
|
||||
} & Omit<TOrgPermission, "orgId">;
|
||||
|
||||
export type TListOrgIdentitiesByOrgIdDTO = {
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
orderBy?: OrgIdentityOrderBy;
|
||||
direction?: OrderByDirection;
|
||||
textFilter?: string;
|
||||
} & TOrgPermission;
|
||||
|
||||
export enum OrgIdentityOrderBy {
|
||||
Name = "name",
|
||||
Role = "role"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user