mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-14 14:34:09 +00:00
feat: completed api for new search identities
This commit is contained in:
@@ -14,10 +14,15 @@ import {
|
||||
TIdentityUniversalAuths,
|
||||
TOrgRoles
|
||||
} from "@app/db/schemas";
|
||||
import { DatabaseError } from "@app/lib/errors";
|
||||
import { BadRequestError, DatabaseError } from "@app/lib/errors";
|
||||
import { ormify, selectAllTableCols, sqlNestRelationships } from "@app/lib/knex";
|
||||
import { buildKnexFilterForSearchResource } from "@app/lib/search-resource/db";
|
||||
import { OrderByDirection } from "@app/lib/types";
|
||||
import { OrgIdentityOrderBy, TListOrgIdentitiesByOrgIdDTO } from "@app/services/identity/identity-types";
|
||||
import {
|
||||
OrgIdentityOrderBy,
|
||||
TListOrgIdentitiesByOrgIdDTO,
|
||||
TSearchOrgIdentitiesByOrgIdDAL
|
||||
} from "@app/services/identity/identity-types";
|
||||
|
||||
import { buildAuthMethods } from "./identity-fns";
|
||||
|
||||
@@ -195,7 +200,6 @@ export const identityOrgDALFactory = (db: TDbClient) => {
|
||||
"paginatedIdentity.identityId",
|
||||
`${TableName.IdentityJwtAuth}.identityId`
|
||||
)
|
||||
|
||||
.select(
|
||||
db.ref("id").withSchema("paginatedIdentity"),
|
||||
db.ref("role").withSchema("paginatedIdentity"),
|
||||
@@ -309,6 +313,214 @@ export const identityOrgDALFactory = (db: TDbClient) => {
|
||||
}
|
||||
};
|
||||
|
||||
const searchIdentities = async (
|
||||
{
|
||||
limit,
|
||||
offset = 0,
|
||||
orderBy = OrgIdentityOrderBy.Name,
|
||||
orderDirection = OrderByDirection.ASC,
|
||||
searchFilter,
|
||||
orgId
|
||||
}: TSearchOrgIdentitiesByOrgIdDAL,
|
||||
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`)
|
||||
.orderBy(`${TableName.Identity}.${orderBy}`, orderDirection)
|
||||
.select(`${TableName.IdentityOrgMembership}.id`)
|
||||
.select<{ id: string; total_count: string }>(
|
||||
db.raw(
|
||||
`count(${TableName.IdentityOrgMembership}."identityId") OVER(PARTITION BY ${TableName.IdentityOrgMembership}."orgId") as total_count`
|
||||
)
|
||||
)
|
||||
.as("searchedIdentities");
|
||||
|
||||
if (searchFilter) {
|
||||
buildKnexFilterForSearchResource(searchQuery, searchFilter, (attr) => {
|
||||
switch (attr) {
|
||||
case "role":
|
||||
return [`${TableName.OrgRoles}.slug`, `${TableName.IdentityOrgMembership}.role`];
|
||||
case "name":
|
||||
return `${TableName.Identity}.name`;
|
||||
default:
|
||||
throw new BadRequestError({ message: `Invalid ${String(attr)} provided` });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (limit) {
|
||||
void searchQuery.offset(offset).limit(limit);
|
||||
}
|
||||
|
||||
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`)
|
||||
.leftJoin(TableName.IdentityMetadata, (queryBuilder) => {
|
||||
void queryBuilder
|
||||
.on(`${TableName.IdentityOrgMembership}.identityId`, `${TableName.IdentityMetadata}.identityId`)
|
||||
.andOn(`${TableName.IdentityOrgMembership}.orgId`, `${TableName.IdentityMetadata}.orgId`);
|
||||
})
|
||||
.leftJoin(
|
||||
TableName.IdentityUniversalAuth,
|
||||
`${TableName.IdentityOrgMembership}.identityId`,
|
||||
`${TableName.IdentityUniversalAuth}.identityId`
|
||||
)
|
||||
.leftJoin(
|
||||
TableName.IdentityGcpAuth,
|
||||
`${TableName.IdentityOrgMembership}.identityId`,
|
||||
`${TableName.IdentityGcpAuth}.identityId`
|
||||
)
|
||||
.leftJoin(
|
||||
TableName.IdentityAwsAuth,
|
||||
`${TableName.IdentityOrgMembership}.identityId`,
|
||||
`${TableName.IdentityAwsAuth}.identityId`
|
||||
)
|
||||
.leftJoin(
|
||||
TableName.IdentityKubernetesAuth,
|
||||
`${TableName.IdentityOrgMembership}.identityId`,
|
||||
`${TableName.IdentityKubernetesAuth}.identityId`
|
||||
)
|
||||
.leftJoin(
|
||||
TableName.IdentityOidcAuth,
|
||||
`${TableName.IdentityOrgMembership}.identityId`,
|
||||
`${TableName.IdentityOidcAuth}.identityId`
|
||||
)
|
||||
.leftJoin(
|
||||
TableName.IdentityAzureAuth,
|
||||
`${TableName.IdentityOrgMembership}.identityId`,
|
||||
`${TableName.IdentityAzureAuth}.identityId`
|
||||
)
|
||||
.leftJoin(
|
||||
TableName.IdentityTokenAuth,
|
||||
`${TableName.IdentityOrgMembership}.identityId`,
|
||||
`${TableName.IdentityTokenAuth}.identityId`
|
||||
)
|
||||
.leftJoin(
|
||||
TableName.IdentityJwtAuth,
|
||||
`${TableName.IdentityOrgMembership}.identityId`,
|
||||
`${TableName.IdentityJwtAuth}.identityId`
|
||||
)
|
||||
.select(
|
||||
db.ref("id").withSchema(TableName.IdentityOrgMembership),
|
||||
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("identityId").withSchema(TableName.IdentityOrgMembership).as("identityId"),
|
||||
db.ref("name").withSchema(TableName.Identity).as("identityName"),
|
||||
|
||||
db.ref("id").as("uaId").withSchema(TableName.IdentityUniversalAuth),
|
||||
db.ref("id").as("gcpId").withSchema(TableName.IdentityGcpAuth),
|
||||
db.ref("id").as("awsId").withSchema(TableName.IdentityAwsAuth),
|
||||
db.ref("id").as("kubernetesId").withSchema(TableName.IdentityKubernetesAuth),
|
||||
db.ref("id").as("oidcId").withSchema(TableName.IdentityOidcAuth),
|
||||
db.ref("id").as("azureId").withSchema(TableName.IdentityAzureAuth),
|
||||
db.ref("id").as("tokenId").withSchema(TableName.IdentityTokenAuth),
|
||||
db.ref("id").as("jwtId").withSchema(TableName.IdentityJwtAuth)
|
||||
)
|
||||
// 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").withSchema(TableName.IdentityMetadata).as("metadataId"),
|
||||
db.ref("key").withSchema(TableName.IdentityMetadata).as("metadataKey"),
|
||||
db.ref("value").withSchema(TableName.IdentityMetadata).as("metadataValue")
|
||||
);
|
||||
|
||||
if (orderBy === OrgIdentityOrderBy.Name) {
|
||||
void query.orderBy("identityName", orderDirection);
|
||||
}
|
||||
|
||||
const docs = await query;
|
||||
const formattedDocs = sqlNestRelationships({
|
||||
data: docs,
|
||||
key: "id",
|
||||
parentMapper: ({
|
||||
crId,
|
||||
crDescription,
|
||||
crSlug,
|
||||
crPermission,
|
||||
crName,
|
||||
identityId,
|
||||
identityName,
|
||||
role,
|
||||
roleId,
|
||||
total_count,
|
||||
id,
|
||||
uaId,
|
||||
awsId,
|
||||
gcpId,
|
||||
jwtId,
|
||||
kubernetesId,
|
||||
oidcId,
|
||||
azureId,
|
||||
tokenId,
|
||||
createdAt,
|
||||
updatedAt
|
||||
}) => ({
|
||||
role,
|
||||
roleId,
|
||||
identityId,
|
||||
id,
|
||||
total_count: total_count as string,
|
||||
orgId,
|
||||
createdAt,
|
||||
updatedAt,
|
||||
customRole: roleId
|
||||
? {
|
||||
id: crId,
|
||||
name: crName,
|
||||
slug: crSlug,
|
||||
permissions: crPermission,
|
||||
description: crDescription
|
||||
}
|
||||
: undefined,
|
||||
identity: {
|
||||
id: identityId,
|
||||
name: identityName,
|
||||
authMethods: buildAuthMethods({
|
||||
uaId,
|
||||
awsId,
|
||||
gcpId,
|
||||
kubernetesId,
|
||||
oidcId,
|
||||
azureId,
|
||||
tokenId,
|
||||
jwtId
|
||||
})
|
||||
}
|
||||
}),
|
||||
childrenMapper: [
|
||||
{
|
||||
key: "metadataId",
|
||||
label: "metadata" as const,
|
||||
mapper: ({ metadataKey, metadataValue, metadataId }) => ({
|
||||
id: metadataId,
|
||||
key: metadataKey,
|
||||
value: metadataValue
|
||||
})
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
return { docs: formattedDocs, totalCount: Number(formattedDocs?.[0]?.total_count ?? 0) };
|
||||
} catch (error) {
|
||||
throw new DatabaseError({ error, name: "FindByOrgId" });
|
||||
}
|
||||
};
|
||||
|
||||
const countAllOrgIdentities = async (
|
||||
{ search, ...filter }: Partial<TIdentityOrgMemberships> & Pick<TListOrgIdentitiesByOrgIdDTO, "search">,
|
||||
tx?: Knex
|
||||
@@ -331,5 +543,5 @@ export const identityOrgDALFactory = (db: TDbClient) => {
|
||||
}
|
||||
};
|
||||
|
||||
return { ...identityOrgOrm, find, findOne, countAllOrgIdentities };
|
||||
return { ...identityOrgOrm, find, findOne, countAllOrgIdentities, searchIdentities };
|
||||
};
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
TGetIdentityByIdDTO,
|
||||
TListOrgIdentitiesByOrgIdDTO,
|
||||
TListProjectIdentitiesByIdentityIdDTO,
|
||||
TSearchOrgIdentitiesByOrgIdDTO,
|
||||
TUpdateIdentityDTO
|
||||
} from "./identity-types";
|
||||
|
||||
@@ -288,6 +289,33 @@ export const identityServiceFactory = ({
|
||||
return { identityMemberships, totalCount };
|
||||
};
|
||||
|
||||
const searchOrgIdentities = async ({
|
||||
orgId,
|
||||
actor,
|
||||
actorId,
|
||||
actorAuthMethod,
|
||||
actorOrgId,
|
||||
limit,
|
||||
offset,
|
||||
orderBy,
|
||||
orderDirection,
|
||||
searchFilter = {}
|
||||
}: TSearchOrgIdentitiesByOrgIdDTO) => {
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity);
|
||||
|
||||
const { totalCount, docs } = await identityOrgMembershipDAL.searchIdentities({
|
||||
orgId,
|
||||
limit,
|
||||
offset,
|
||||
orderBy,
|
||||
orderDirection,
|
||||
searchFilter
|
||||
});
|
||||
|
||||
return { identityMemberships: docs, totalCount };
|
||||
};
|
||||
|
||||
const listProjectIdentitiesByIdentityId = async ({
|
||||
identityId,
|
||||
actor,
|
||||
@@ -317,6 +345,7 @@ export const identityServiceFactory = ({
|
||||
deleteIdentity,
|
||||
listOrgIdentities,
|
||||
getIdentityById,
|
||||
searchOrgIdentities,
|
||||
listProjectIdentitiesByIdentityId
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { IPType } from "@app/lib/ip";
|
||||
import { TSearchResourceOperator } from "@app/lib/search-resource/search";
|
||||
import { OrderByDirection, TOrgPermission } from "@app/lib/types";
|
||||
|
||||
export type TCreateIdentityDTO = {
|
||||
@@ -46,3 +47,17 @@ export enum OrgIdentityOrderBy {
|
||||
Name = "name"
|
||||
// Role = "role"
|
||||
}
|
||||
|
||||
export type TSearchOrgIdentitiesByOrgIdDAL = {
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
orderBy?: OrgIdentityOrderBy;
|
||||
orderDirection?: OrderByDirection;
|
||||
orgId: string;
|
||||
searchFilter?: Partial<{
|
||||
name: Omit<TSearchResourceOperator, "number">;
|
||||
role: Omit<TSearchResourceOperator, "number">;
|
||||
}>;
|
||||
};
|
||||
|
||||
export type TSearchOrgIdentitiesByOrgIdDTO = TSearchOrgIdentitiesByOrgIdDAL & TOrgPermission;
|
||||
|
||||
Reference in New Issue
Block a user