From 605ccb13e98b7e5d2b65941d5632726c6fe18f9c Mon Sep 17 00:00:00 2001 From: = Date: Tue, 18 Jun 2024 16:44:12 +0530 Subject: [PATCH] feat: added endpoints and docs for identity get by id and list operation --- backend/src/lib/api-docs/constants.ts | 7 ++ .../src/server/routes/v1/identity-router.ts | 92 ++++++++++++++++++- .../src/services/identity/identity-org-dal.ts | 6 +- .../src/services/identity/identity-service.ts | 29 +++++- .../src/services/identity/identity-types.ts | 4 + .../endpoints/identities/get-by-id.mdx | 5 + .../endpoints/identities/list.mdx | 4 + docs/mint.json | 6 +- 8 files changed, 143 insertions(+), 10 deletions(-) create mode 100644 docs/api-reference/endpoints/identities/get-by-id.mdx create mode 100644 docs/api-reference/endpoints/identities/list.mdx diff --git a/backend/src/lib/api-docs/constants.ts b/backend/src/lib/api-docs/constants.ts index d768066fa..3ac4e5734 100644 --- a/backend/src/lib/api-docs/constants.ts +++ b/backend/src/lib/api-docs/constants.ts @@ -42,6 +42,13 @@ export const IDENTITIES = { }, DELETE: { identityId: "The ID of the identity to delete." + }, + GET_BY_ID: { + identityId: "The ID of the identity to get details.", + orgId: "The ID of the org of the identity" + }, + LIST: { + orgId: "The ID of the organization to list identities." } } as const; diff --git a/backend/src/server/routes/v1/identity-router.ts b/backend/src/server/routes/v1/identity-router.ts index e174cf974..158261eb1 100644 --- a/backend/src/server/routes/v1/identity-router.ts +++ b/backend/src/server/routes/v1/identity-router.ts @@ -1,6 +1,6 @@ import { z } from "zod"; -import { IdentitiesSchema, OrgMembershipRole } from "@app/db/schemas"; +import { IdentitiesSchema, IdentityOrgMembershipsSchema, OrgMembershipRole, OrgRolesSchema } from "@app/db/schemas"; import { EventType } from "@app/ee/services/audit-log/audit-log-types"; import { IDENTITIES } from "@app/lib/api-docs"; import { creationLimit, writeLimit } from "@app/server/config/rateLimiter"; @@ -170,4 +170,94 @@ export const registerIdentityRouter = async (server: FastifyZodProvider) => { return { identity }; } }); + + server.route({ + method: "GET", + url: "/:identityId", + config: { + rateLimit: writeLimit + }, + onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), + schema: { + description: "Get an identity by id", + security: [ + { + bearerAuth: [] + } + ], + params: z.object({ + identityId: z.string().describe(IDENTITIES.GET_BY_ID.identityId) + }), + response: { + 200: z.object({ + identity: IdentityOrgMembershipsSchema.extend({ + customRole: OrgRolesSchema.pick({ + id: true, + name: true, + slug: true, + permissions: true, + description: true + }).optional(), + identity: IdentitiesSchema.pick({ name: true, id: true, authMethod: true }) + }) + }) + } + }, + handler: async (req) => { + const identity = await server.services.identity.getIdentityById({ + actor: req.permission.type, + actorId: req.permission.id, + actorAuthMethod: req.permission.authMethod, + actorOrgId: req.permission.orgId, + id: req.params.identityId + }); + + return { identity }; + } + }); + + server.route({ + method: "GET", + url: "/", + config: { + rateLimit: writeLimit + }, + onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), + schema: { + description: "List identities", + security: [ + { + bearerAuth: [] + } + ], + querystring: z.object({ + orgId: z.string().describe(IDENTITIES.LIST.orgId) + }), + response: { + 200: z.object({ + identities: IdentityOrgMembershipsSchema.extend({ + customRole: OrgRolesSchema.pick({ + id: true, + name: true, + slug: true, + permissions: true, + description: true + }).optional(), + identity: IdentitiesSchema.pick({ name: true, id: true, authMethod: true }) + }).array() + }) + } + }, + handler: async (req) => { + const identities = await server.services.identity.listOrgIdentities({ + actor: req.permission.type, + actorId: req.permission.id, + actorAuthMethod: req.permission.authMethod, + actorOrgId: req.permission.orgId, + orgId: req.query.orgId + }); + + return { identities }; + } + }); }; diff --git a/backend/src/services/identity/identity-org-dal.ts b/backend/src/services/identity/identity-org-dal.ts index 95d742f33..0d3199725 100644 --- a/backend/src/services/identity/identity-org-dal.ts +++ b/backend/src/services/identity/identity-org-dal.ts @@ -27,10 +27,10 @@ export const identityOrgDALFactory = (db: TDbClient) => { } }; - const findByOrgId = async (orgId: string, tx?: Knex) => { + const find = async (filter: Partial, tx?: Knex) => { try { const docs = await (tx || db)(TableName.IdentityOrgMembership) - .where(`${TableName.IdentityOrgMembership}.orgId`, orgId) + .where(filter) .join(TableName.Identity, `${TableName.IdentityOrgMembership}.identityId`, `${TableName.Identity}.id`) .leftJoin(TableName.OrgRoles, `${TableName.IdentityOrgMembership}.roleId`, `${TableName.OrgRoles}.id`) .select(selectAllTableCols(TableName.IdentityOrgMembership)) @@ -79,5 +79,5 @@ export const identityOrgDALFactory = (db: TDbClient) => { } }; - return { ...identityOrgOrm, findOne, findByOrgId }; + return { ...identityOrgOrm, find, findOne }; }; diff --git a/backend/src/services/identity/identity-service.ts b/backend/src/services/identity/identity-service.ts index 2863bf23e..7d44cfdd4 100644 --- a/backend/src/services/identity/identity-service.ts +++ b/backend/src/services/identity/identity-service.ts @@ -1,6 +1,6 @@ import { ForbiddenError } from "@casl/ability"; -import { OrgMembershipRole, TOrgRoles } from "@app/db/schemas"; +import { OrgMembershipRole, TableName, TOrgRoles } from "@app/db/schemas"; import { OrgPermissionActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; import { isAtLeastAsPrivileged } from "@app/lib/casl"; @@ -10,7 +10,7 @@ import { TOrgPermission } from "@app/lib/types"; import { ActorType } from "../auth/auth-type"; import { TIdentityDALFactory } from "./identity-dal"; import { TIdentityOrgDALFactory } from "./identity-org-dal"; -import { TCreateIdentityDTO, TDeleteIdentityDTO, TUpdateIdentityDTO } from "./identity-types"; +import { TCreateIdentityDTO, TDeleteIdentityDTO, TGetIdentityByIdDTO, TUpdateIdentityDTO } from "./identity-types"; type TIdentityServiceFactoryDep = { identityDAL: TIdentityDALFactory; @@ -126,6 +126,24 @@ export const identityServiceFactory = ({ return { ...identity, orgId: identityOrgMembership.orgId }; }; + const getIdentityById = async ({ id, actor, actorId, actorOrgId, actorAuthMethod }: TGetIdentityByIdDTO) => { + const doc = await identityOrgMembershipDAL.find({ + [`${TableName.IdentityOrgMembership}.identityId` as "identityId"]: id + }); + const identity = doc[0]; + if (!identity) throw new BadRequestError({ message: `Failed to find identity with id ${id}` }); + + const { permission } = await permissionService.getOrgPermission( + actor, + actorId, + identity.orgId, + actorAuthMethod, + actorOrgId + ); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Identity); + return identity; + }; + const deleteIdentity = async ({ actorId, actor, actorOrgId, actorAuthMethod, id }: TDeleteIdentityDTO) => { const identityOrgMembership = await identityOrgMembershipDAL.findOne({ identityId: id }); if (!identityOrgMembership) throw new BadRequestError({ message: `Failed to find identity with id ${id}` }); @@ -157,7 +175,9 @@ export const identityServiceFactory = ({ const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Identity); - const identityMemberships = await identityOrgMembershipDAL.findByOrgId(orgId); + const identityMemberships = await identityOrgMembershipDAL.find({ + [`${TableName.IdentityOrgMembership}.orgId` as "orgId"]: orgId + }); return identityMemberships; }; @@ -165,6 +185,7 @@ export const identityServiceFactory = ({ createIdentity, updateIdentity, deleteIdentity, - listOrgIdentities + listOrgIdentities, + getIdentityById }; }; diff --git a/backend/src/services/identity/identity-types.ts b/backend/src/services/identity/identity-types.ts index 10b943667..5125413e8 100644 --- a/backend/src/services/identity/identity-types.ts +++ b/backend/src/services/identity/identity-types.ts @@ -16,6 +16,10 @@ export type TDeleteIdentityDTO = { id: string; } & Omit; +export type TGetIdentityByIdDTO = { + id: string; +} & Omit; + export interface TIdentityTrustedIp { ipAddress: string; type: IPType; diff --git a/docs/api-reference/endpoints/identities/get-by-id.mdx b/docs/api-reference/endpoints/identities/get-by-id.mdx new file mode 100644 index 000000000..f721d3556 --- /dev/null +++ b/docs/api-reference/endpoints/identities/get-by-id.mdx @@ -0,0 +1,5 @@ +--- +title: "Get By ID" +openapi: "GET /api/v1/identities/{identityId}" +--- + diff --git a/docs/api-reference/endpoints/identities/list.mdx b/docs/api-reference/endpoints/identities/list.mdx new file mode 100644 index 000000000..d8972e3a9 --- /dev/null +++ b/docs/api-reference/endpoints/identities/list.mdx @@ -0,0 +1,4 @@ +--- +title: "List" +openapi: "GET /api/v1/identities" +--- diff --git a/docs/mint.json b/docs/mint.json index 1caad0715..9e07b01b1 100644 --- a/docs/mint.json +++ b/docs/mint.json @@ -1,6 +1,6 @@ { "name": "Infisical", - "openapi": "https://app.infisical.com/api/docs/json", + "openapi": "http://localhost:8080/api/docs/json", "logo": { "dark": "/logo/dark.svg", "light": "/logo/light.svg", @@ -416,7 +416,9 @@ "pages": [ "api-reference/endpoints/identities/create", "api-reference/endpoints/identities/update", - "api-reference/endpoints/identities/delete" + "api-reference/endpoints/identities/delete", + "api-reference/endpoints/identities/get-by-id", + "api-reference/endpoints/identities/list" ] }, {