feat: added endpoint for oidc auth revocation

This commit is contained in:
Sheen Capadngan
2024-07-04 00:53:24 +08:00
parent 2153dd94eb
commit 4927cc804a
4 changed files with 117 additions and 3 deletions
+6
View File
@@ -142,6 +142,12 @@ export const KUBERNETES_AUTH = {
} }
} as const; } as const;
export const OIDC_AUTH = {
REVOKE: {
identityId: "The ID of the identity to revoke."
}
} as const;
export const ORGANIZATIONS = { export const ORGANIZATIONS = {
LIST_USER_MEMBERSHIPS: { LIST_USER_MEMBERSHIPS: {
organizationId: "The ID of the organization to get memberships from." organizationId: "The ID of the organization to get memberships from."
@@ -2,6 +2,7 @@ import { z } from "zod";
import { IdentityOidcAuthsSchema } from "@app/db/schemas"; import { IdentityOidcAuthsSchema } from "@app/db/schemas";
import { EventType } from "@app/ee/services/audit-log/audit-log-types"; import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { OIDC_AUTH } from "@app/lib/api-docs";
import { readLimit, writeLimit } from "@app/server/config/rateLimiter"; import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type"; import { AuthMode } from "@app/services/auth/auth-type";
@@ -297,4 +298,53 @@ export const registerIdentityOidcAuthRouter = async (server: FastifyZodProvider)
return { identityOidcAuth }; return { identityOidcAuth };
} }
}); });
server.route({
method: "DELETE",
url: "/oidc-auth/identities/:identityId",
config: {
rateLimit: writeLimit
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
description: "Delete OIDC Auth configuration on identity",
security: [
{
bearerAuth: []
}
],
params: z.object({
identityId: z.string().describe(OIDC_AUTH.REVOKE.identityId)
}),
response: {
200: z.object({
identityOidcAuth: IdentityOidcAuthResponseSchema.omit({
caCert: true
})
})
}
},
handler: async (req) => {
const identityOidcAuth = await server.services.identityOidcAuth.revokeOidcAuth({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
identityId: req.params.identityId
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
orgId: identityOidcAuth.orgId,
event: {
type: EventType.REVOKE_IDENTITY_OIDC_AUTH,
metadata: {
identityId: identityOidcAuth.identityId
}
}
});
return { identityOidcAuth };
}
});
}; };
@@ -8,6 +8,7 @@ import { IdentityAuthMethod, SecretKeyEncoding, TIdentityOidcAuthsUpdate } from
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service"; import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission"; import { OrgPermissionActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
import { isAtLeastAsPrivileged } from "@app/lib/casl";
import { getConfig } from "@app/lib/config/env"; import { getConfig } from "@app/lib/config/env";
import { generateAsymmetricKeyPair } from "@app/lib/crypto"; import { generateAsymmetricKeyPair } from "@app/lib/crypto";
import { import {
@@ -17,17 +18,23 @@ import {
infisicalSymmetricDecrypt, infisicalSymmetricDecrypt,
infisicalSymmetricEncypt infisicalSymmetricEncypt
} from "@app/lib/crypto/encryption"; } from "@app/lib/crypto/encryption";
import { BadRequestError, UnauthorizedError } from "@app/lib/errors"; import { BadRequestError, ForbiddenRequestError, UnauthorizedError } from "@app/lib/errors";
import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip"; import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
import { AuthTokenType } from "../auth/auth-type"; import { ActorType, AuthTokenType } from "../auth/auth-type";
import { TIdentityDALFactory } from "../identity/identity-dal"; import { TIdentityDALFactory } from "../identity/identity-dal";
import { TIdentityOrgDALFactory } from "../identity/identity-org-dal"; import { TIdentityOrgDALFactory } from "../identity/identity-org-dal";
import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal"; import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal";
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types"; import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
import { TOrgBotDALFactory } from "../org/org-bot-dal"; import { TOrgBotDALFactory } from "../org/org-bot-dal";
import { TIdentityOidcAuthDALFactory } from "./identity-oidc-auth-dal"; import { TIdentityOidcAuthDALFactory } from "./identity-oidc-auth-dal";
import { TAttachOidcAuthDTO, TGetOidcAuthDTO, TLoginOidcAuthDTO, TUpdateOidcAuthDTO } from "./identity-oidc-auth-types"; import {
TAttachOidcAuthDTO,
TGetOidcAuthDTO,
TLoginOidcAuthDTO,
TRevokeOidcAuthDTO,
TUpdateOidcAuthDTO
} from "./identity-oidc-auth-types";
type TIdentityOidcAuthServiceFactoryDep = { type TIdentityOidcAuthServiceFactoryDep = {
identityOidcAuthDAL: TIdentityOidcAuthDALFactory; identityOidcAuthDAL: TIdentityOidcAuthDALFactory;
@@ -471,10 +478,57 @@ export const identityOidcAuthServiceFactory = ({
return { ...identityOidcAuth, orgId: identityMembershipOrg.orgId, caCert }; return { ...identityOidcAuth, orgId: identityMembershipOrg.orgId, caCert };
}; };
const revokeOidcAuth = async ({ identityId, actorId, actor, actorAuthMethod, actorOrgId }: TRevokeOidcAuthDTO) => {
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
if (!identityMembershipOrg) {
throw new BadRequestError({ message: "Failed to find identity" });
}
if (identityMembershipOrg.identity?.authMethod !== IdentityAuthMethod.OIDC_AUTH) {
throw new BadRequestError({
message: "The identity does not have OIDC auth"
});
}
const { permission } = await permissionService.getOrgPermission(
actor,
actorId,
identityMembershipOrg.orgId,
actorAuthMethod,
actorOrgId
);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Identity);
const { permission: rolePermission } = await permissionService.getOrgPermission(
ActorType.IDENTITY,
identityMembershipOrg.identityId,
identityMembershipOrg.orgId,
actorAuthMethod,
actorOrgId
);
const hasPriviledge = isAtLeastAsPrivileged(permission, rolePermission);
if (!hasPriviledge) {
throw new ForbiddenRequestError({
message: "Failed to revoke OIDC auth of identity with more privileged role"
});
}
const revokedIdentityOidcAuth = await identityOidcAuthDAL.transaction(async (tx) => {
const deletedOidcAuth = await identityOidcAuthDAL.delete({ identityId }, tx);
await identityDAL.updateById(identityId, { authMethod: null }, tx);
return { ...deletedOidcAuth?.[0], orgId: identityMembershipOrg.orgId };
});
return revokedIdentityOidcAuth;
};
return { return {
attachOidcAuth, attachOidcAuth,
updateOidcAuth, updateOidcAuth,
getOidcAuth, getOidcAuth,
revokeOidcAuth,
login login
}; };
}; };
@@ -36,3 +36,7 @@ export type TLoginOidcAuthDTO = {
identityId: string; identityId: string;
jwt: string; jwt: string;
}; };
export type TRevokeOidcAuthDTO = {
identityId: string;
} & Omit<TProjectPermission, "projectId">;