mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-05 15:26:35 +00:00
feat: finished up oidc auth management
This commit is contained in:
Vendored
+2
@@ -41,6 +41,7 @@ import { TIdentityAwsAuthServiceFactory } from "@app/services/identity-aws-auth/
|
|||||||
import { TIdentityAzureAuthServiceFactory } from "@app/services/identity-azure-auth/identity-azure-auth-service";
|
import { TIdentityAzureAuthServiceFactory } from "@app/services/identity-azure-auth/identity-azure-auth-service";
|
||||||
import { TIdentityGcpAuthServiceFactory } from "@app/services/identity-gcp-auth/identity-gcp-auth-service";
|
import { TIdentityGcpAuthServiceFactory } from "@app/services/identity-gcp-auth/identity-gcp-auth-service";
|
||||||
import { TIdentityKubernetesAuthServiceFactory } from "@app/services/identity-kubernetes-auth/identity-kubernetes-auth-service";
|
import { TIdentityKubernetesAuthServiceFactory } from "@app/services/identity-kubernetes-auth/identity-kubernetes-auth-service";
|
||||||
|
import { TIdentityOidcAuthServiceFactory } from "@app/services/identity-oidc-auth/identity-oidc-auth-service";
|
||||||
import { TIdentityProjectServiceFactory } from "@app/services/identity-project/identity-project-service";
|
import { TIdentityProjectServiceFactory } from "@app/services/identity-project/identity-project-service";
|
||||||
import { TIdentityUaServiceFactory } from "@app/services/identity-ua/identity-ua-service";
|
import { TIdentityUaServiceFactory } from "@app/services/identity-ua/identity-ua-service";
|
||||||
import { TIntegrationServiceFactory } from "@app/services/integration/integration-service";
|
import { TIntegrationServiceFactory } from "@app/services/integration/integration-service";
|
||||||
@@ -132,6 +133,7 @@ declare module "fastify" {
|
|||||||
identityGcpAuth: TIdentityGcpAuthServiceFactory;
|
identityGcpAuth: TIdentityGcpAuthServiceFactory;
|
||||||
identityAwsAuth: TIdentityAwsAuthServiceFactory;
|
identityAwsAuth: TIdentityAwsAuthServiceFactory;
|
||||||
identityAzureAuth: TIdentityAzureAuthServiceFactory;
|
identityAzureAuth: TIdentityAzureAuthServiceFactory;
|
||||||
|
identityOidcAuth: TIdentityOidcAuthServiceFactory;
|
||||||
accessApprovalPolicy: TAccessApprovalPolicyServiceFactory;
|
accessApprovalPolicy: TAccessApprovalPolicyServiceFactory;
|
||||||
accessApprovalRequest: TAccessApprovalRequestServiceFactory;
|
accessApprovalRequest: TAccessApprovalRequestServiceFactory;
|
||||||
secretApprovalPolicy: TSecretApprovalPolicyServiceFactory;
|
secretApprovalPolicy: TSecretApprovalPolicyServiceFactory;
|
||||||
|
|||||||
@@ -166,5 +166,6 @@ export enum IdentityAuthMethod {
|
|||||||
KUBERNETES_AUTH = "kubernetes-auth",
|
KUBERNETES_AUTH = "kubernetes-auth",
|
||||||
GCP_AUTH = "gcp-auth",
|
GCP_AUTH = "gcp-auth",
|
||||||
AWS_AUTH = "aws-auth",
|
AWS_AUTH = "aws-auth",
|
||||||
AZURE_AUTH = "azure-auth"
|
AZURE_AUTH = "azure-auth",
|
||||||
|
OIDC_AUTH = "oidc-auth"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -102,6 +102,8 @@ import { identityGcpAuthDALFactory } from "@app/services/identity-gcp-auth/ident
|
|||||||
import { identityGcpAuthServiceFactory } from "@app/services/identity-gcp-auth/identity-gcp-auth-service";
|
import { identityGcpAuthServiceFactory } from "@app/services/identity-gcp-auth/identity-gcp-auth-service";
|
||||||
import { identityKubernetesAuthDALFactory } from "@app/services/identity-kubernetes-auth/identity-kubernetes-auth-dal";
|
import { identityKubernetesAuthDALFactory } from "@app/services/identity-kubernetes-auth/identity-kubernetes-auth-dal";
|
||||||
import { identityKubernetesAuthServiceFactory } from "@app/services/identity-kubernetes-auth/identity-kubernetes-auth-service";
|
import { identityKubernetesAuthServiceFactory } from "@app/services/identity-kubernetes-auth/identity-kubernetes-auth-service";
|
||||||
|
import { identityOidcAuthDALFactory } from "@app/services/identity-oidc-auth/identity-oidc-auth-dal";
|
||||||
|
import { identityOidcAuthServiceFactory } from "@app/services/identity-oidc-auth/identity-oidc-auth-service";
|
||||||
import { identityProjectDALFactory } from "@app/services/identity-project/identity-project-dal";
|
import { identityProjectDALFactory } from "@app/services/identity-project/identity-project-dal";
|
||||||
import { identityProjectMembershipRoleDALFactory } from "@app/services/identity-project/identity-project-membership-role-dal";
|
import { identityProjectMembershipRoleDALFactory } from "@app/services/identity-project/identity-project-membership-role-dal";
|
||||||
import { identityProjectServiceFactory } from "@app/services/identity-project/identity-project-service";
|
import { identityProjectServiceFactory } from "@app/services/identity-project/identity-project-service";
|
||||||
@@ -238,6 +240,7 @@ export const registerRoutes = async (
|
|||||||
const identityUaClientSecretDAL = identityUaClientSecretDALFactory(db);
|
const identityUaClientSecretDAL = identityUaClientSecretDALFactory(db);
|
||||||
const identityAwsAuthDAL = identityAwsAuthDALFactory(db);
|
const identityAwsAuthDAL = identityAwsAuthDALFactory(db);
|
||||||
const identityGcpAuthDAL = identityGcpAuthDALFactory(db);
|
const identityGcpAuthDAL = identityGcpAuthDALFactory(db);
|
||||||
|
const identityOidcAuthDAL = identityOidcAuthDALFactory(db);
|
||||||
const identityAzureAuthDAL = identityAzureAuthDALFactory(db);
|
const identityAzureAuthDAL = identityAzureAuthDALFactory(db);
|
||||||
|
|
||||||
const auditLogDAL = auditLogDALFactory(db);
|
const auditLogDAL = auditLogDALFactory(db);
|
||||||
@@ -874,6 +877,16 @@ export const registerRoutes = async (
|
|||||||
licenseService
|
licenseService
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const identityOidcAuthService = identityOidcAuthServiceFactory({
|
||||||
|
identityOidcAuthDAL,
|
||||||
|
identityOrgMembershipDAL,
|
||||||
|
identityAccessTokenDAL,
|
||||||
|
identityDAL,
|
||||||
|
permissionService,
|
||||||
|
licenseService,
|
||||||
|
orgBotDAL
|
||||||
|
});
|
||||||
|
|
||||||
const dynamicSecretProviders = buildDynamicSecretProviders();
|
const dynamicSecretProviders = buildDynamicSecretProviders();
|
||||||
const dynamicSecretQueueService = dynamicSecretLeaseQueueServiceFactory({
|
const dynamicSecretQueueService = dynamicSecretLeaseQueueServiceFactory({
|
||||||
queueService,
|
queueService,
|
||||||
@@ -972,6 +985,7 @@ export const registerRoutes = async (
|
|||||||
identityGcpAuth: identityGcpAuthService,
|
identityGcpAuth: identityGcpAuthService,
|
||||||
identityAwsAuth: identityAwsAuthService,
|
identityAwsAuth: identityAwsAuthService,
|
||||||
identityAzureAuth: identityAzureAuthService,
|
identityAzureAuth: identityAzureAuthService,
|
||||||
|
identityOidcAuth: identityOidcAuthService,
|
||||||
accessApprovalPolicy: accessApprovalPolicyService,
|
accessApprovalPolicy: accessApprovalPolicyService,
|
||||||
accessApprovalRequest: accessApprovalRequestService,
|
accessApprovalRequest: accessApprovalRequestService,
|
||||||
secretApprovalPolicy: secretApprovalPolicyService,
|
secretApprovalPolicy: secretApprovalPolicyService,
|
||||||
|
|||||||
@@ -1,11 +1,19 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { IdentityOidcAuthsSchema } from "@app/db/schemas";
|
import { IdentityOidcAuthsSchema } from "@app/db/schemas";
|
||||||
import { 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";
|
||||||
import { validateOidcAuthAudiencesField } from "@app/services/identity-oidc-auth/identity-oidc-auth-validators";
|
import { validateOidcAuthAudiencesField } from "@app/services/identity-oidc-auth/identity-oidc-auth-validators";
|
||||||
|
|
||||||
|
const IdentityOidcAuthResponseSchema = IdentityOidcAuthsSchema.omit({
|
||||||
|
encryptedCaCert: true,
|
||||||
|
caCertIV: true,
|
||||||
|
caCertTag: true
|
||||||
|
}).extend({
|
||||||
|
caCert: z.string()
|
||||||
|
});
|
||||||
|
|
||||||
export const registerIdentityOidcAuthRouter = async (server: FastifyZodProvider) => {
|
export const registerIdentityOidcAuthRouter = async (server: FastifyZodProvider) => {
|
||||||
server.route({
|
server.route({
|
||||||
method: "POST",
|
method: "POST",
|
||||||
@@ -53,14 +61,133 @@ export const registerIdentityOidcAuthRouter = async (server: FastifyZodProvider)
|
|||||||
boundIssuer: z.string().min(1),
|
boundIssuer: z.string().min(1),
|
||||||
boundAudiences: validateOidcAuthAudiencesField,
|
boundAudiences: validateOidcAuthAudiencesField,
|
||||||
boundClaims: z.record(z.string()),
|
boundClaims: z.record(z.string()),
|
||||||
boundSubject: z.string().optional()
|
boundSubject: z.string().optional().default("")
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
identityOidcAuth: IdentityOidcAuthsSchema
|
identityOidcAuth: IdentityOidcAuthResponseSchema
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
handler: async () => {}
|
handler: async (req) => {
|
||||||
|
const identityOidcAuth = await server.services.identityOidcAuth.attachOidcAuth({
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actorAuthMethod: req.permission.authMethod,
|
||||||
|
actorOrgId: req.permission.orgId,
|
||||||
|
...req.body,
|
||||||
|
identityId: req.params.identityId
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
identityOidcAuth
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "PATCH",
|
||||||
|
url: "/oidc-auth/identities/:identityId",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
schema: {
|
||||||
|
description: "Update OIDC Auth configuration on identity",
|
||||||
|
security: [
|
||||||
|
{
|
||||||
|
bearerAuth: []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
params: z.object({
|
||||||
|
identityId: z.string().trim()
|
||||||
|
}),
|
||||||
|
body: z
|
||||||
|
.object({
|
||||||
|
accessTokenTrustedIps: z
|
||||||
|
.object({
|
||||||
|
ipAddress: z.string().trim()
|
||||||
|
})
|
||||||
|
.array()
|
||||||
|
.min(1)
|
||||||
|
.default([{ ipAddress: "0.0.0.0/0" }, { ipAddress: "::/0" }]),
|
||||||
|
accessTokenTTL: z
|
||||||
|
.number()
|
||||||
|
.int()
|
||||||
|
.min(1)
|
||||||
|
.refine((value) => value !== 0, {
|
||||||
|
message: "accessTokenTTL must have a non zero number"
|
||||||
|
})
|
||||||
|
.default(2592000),
|
||||||
|
accessTokenMaxTTL: z
|
||||||
|
.number()
|
||||||
|
.int()
|
||||||
|
.refine((value) => value !== 0, {
|
||||||
|
message: "accessTokenMaxTTL must have a non zero number"
|
||||||
|
})
|
||||||
|
.default(2592000),
|
||||||
|
accessTokenNumUsesLimit: z.number().int().min(0).default(0),
|
||||||
|
oidcDiscoveryUrl: z.string().url().min(1),
|
||||||
|
caCert: z.string().trim().default(""),
|
||||||
|
boundIssuer: z.string().min(1),
|
||||||
|
boundAudiences: validateOidcAuthAudiencesField,
|
||||||
|
boundClaims: z.record(z.string()),
|
||||||
|
boundSubject: z.string().optional().default("")
|
||||||
|
})
|
||||||
|
.partial(),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
identityOidcAuth: IdentityOidcAuthResponseSchema
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handler: async (req) => {
|
||||||
|
const identityOidcAuth = await server.services.identityOidcAuth.updateOidcAuth({
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actorOrgId: req.permission.orgId,
|
||||||
|
actorAuthMethod: req.permission.authMethod,
|
||||||
|
...req.body,
|
||||||
|
identityId: req.params.identityId
|
||||||
|
});
|
||||||
|
|
||||||
|
return { identityOidcAuth };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "GET",
|
||||||
|
url: "/oidc-auth/identities/:identityId",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
schema: {
|
||||||
|
description: "Retrieve OIDC Auth configuration on identity",
|
||||||
|
security: [
|
||||||
|
{
|
||||||
|
bearerAuth: []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
params: z.object({
|
||||||
|
identityId: z.string()
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
identityOidcAuth: IdentityOidcAuthResponseSchema
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handler: async (req) => {
|
||||||
|
const identityOidcAuth = await server.services.identityOidcAuth.getOidcAuth({
|
||||||
|
identityId: req.params.identityId,
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actorOrgId: req.permission.orgId,
|
||||||
|
actorAuthMethod: req.permission.authMethod
|
||||||
|
});
|
||||||
|
|
||||||
|
return { identityOidcAuth };
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,11 +1,350 @@
|
|||||||
|
import { ForbiddenError } from "@casl/ability";
|
||||||
|
|
||||||
|
import { IdentityAuthMethod, SecretKeyEncoding, TIdentityOidcAuthsUpdate } from "@app/db/schemas";
|
||||||
|
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||||
|
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
|
||||||
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||||
|
import { generateAsymmetricKeyPair } from "@app/lib/crypto";
|
||||||
|
import {
|
||||||
|
decryptSymmetric,
|
||||||
|
encryptSymmetric,
|
||||||
|
generateSymmetricKey,
|
||||||
|
infisicalSymmetricDecrypt,
|
||||||
|
infisicalSymmetricEncypt
|
||||||
|
} from "@app/lib/crypto/encryption";
|
||||||
|
import { BadRequestError } from "@app/lib/errors";
|
||||||
|
import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
|
||||||
|
|
||||||
|
import { TIdentityDALFactory } from "../identity/identity-dal";
|
||||||
|
import { TIdentityOrgDALFactory } from "../identity/identity-org-dal";
|
||||||
|
import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-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, TUpdateOidcAuthDTO } from "./identity-oidc-auth-types";
|
||||||
|
|
||||||
type TIdentityOidcAuthServiceFactoryDep = {
|
type TIdentityOidcAuthServiceFactoryDep = {
|
||||||
identityOidcAuthDAL: TIdentityOidcAuthDALFactory;
|
identityOidcAuthDAL: TIdentityOidcAuthDALFactory;
|
||||||
|
identityOrgMembershipDAL: Pick<TIdentityOrgDALFactory, "findOne">;
|
||||||
|
identityAccessTokenDAL: Pick<TIdentityAccessTokenDALFactory, "create">;
|
||||||
|
identityDAL: Pick<TIdentityDALFactory, "updateById">;
|
||||||
|
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
||||||
|
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||||
|
orgBotDAL: Pick<TOrgBotDALFactory, "findOne" | "transaction" | "create">;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TIdentityOidcAuthServiceFactory = ReturnType<typeof identityOidcAuthServiceFactory>;
|
export type TIdentityOidcAuthServiceFactory = ReturnType<typeof identityOidcAuthServiceFactory>;
|
||||||
|
|
||||||
export const identityOidcAuthServiceFactory = ({ identityOidcAuthDAL }: TIdentityOidcAuthServiceFactoryDep) => {
|
export const identityOidcAuthServiceFactory = ({
|
||||||
return {};
|
identityOidcAuthDAL,
|
||||||
|
identityOrgMembershipDAL,
|
||||||
|
identityDAL,
|
||||||
|
permissionService,
|
||||||
|
licenseService,
|
||||||
|
orgBotDAL
|
||||||
|
}: TIdentityOidcAuthServiceFactoryDep) => {
|
||||||
|
const attachOidcAuth = async ({
|
||||||
|
identityId,
|
||||||
|
oidcDiscoveryUrl,
|
||||||
|
caCert,
|
||||||
|
boundIssuer,
|
||||||
|
boundAudiences,
|
||||||
|
boundClaims,
|
||||||
|
boundSubject,
|
||||||
|
accessTokenTTL,
|
||||||
|
accessTokenMaxTTL,
|
||||||
|
accessTokenNumUsesLimit,
|
||||||
|
accessTokenTrustedIps,
|
||||||
|
actorId,
|
||||||
|
actorAuthMethod,
|
||||||
|
actor,
|
||||||
|
actorOrgId
|
||||||
|
}: TAttachOidcAuthDTO) => {
|
||||||
|
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
||||||
|
if (!identityMembershipOrg) {
|
||||||
|
throw new BadRequestError({ message: "Failed to find identity" });
|
||||||
|
}
|
||||||
|
if (identityMembershipOrg.identity.authMethod)
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "Failed to add OIDC Auth to already configured identity"
|
||||||
|
});
|
||||||
|
|
||||||
|
if (accessTokenMaxTTL > 0 && accessTokenTTL > accessTokenMaxTTL) {
|
||||||
|
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
||||||
|
}
|
||||||
|
|
||||||
|
const { permission } = await permissionService.getOrgPermission(
|
||||||
|
actor,
|
||||||
|
actorId,
|
||||||
|
identityMembershipOrg.orgId,
|
||||||
|
actorAuthMethod,
|
||||||
|
actorOrgId
|
||||||
|
);
|
||||||
|
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Identity);
|
||||||
|
|
||||||
|
const plan = await licenseService.getPlan(identityMembershipOrg.orgId);
|
||||||
|
const reformattedAccessTokenTrustedIps = accessTokenTrustedIps.map((accessTokenTrustedIp) => {
|
||||||
|
if (
|
||||||
|
!plan.ipAllowlisting &&
|
||||||
|
accessTokenTrustedIp.ipAddress !== "0.0.0.0/0" &&
|
||||||
|
accessTokenTrustedIp.ipAddress !== "::/0"
|
||||||
|
)
|
||||||
|
throw new BadRequestError({
|
||||||
|
message:
|
||||||
|
"Failed to add IP access range to access token due to plan restriction. Upgrade plan to add IP access range."
|
||||||
|
});
|
||||||
|
if (!isValidIpOrCidr(accessTokenTrustedIp.ipAddress))
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "The IP is not a valid IPv4, IPv6, or CIDR block"
|
||||||
|
});
|
||||||
|
return extractIPDetails(accessTokenTrustedIp.ipAddress);
|
||||||
|
});
|
||||||
|
|
||||||
|
const orgBot = await orgBotDAL.transaction(async (tx) => {
|
||||||
|
const doc = await orgBotDAL.findOne({ orgId: identityMembershipOrg.orgId }, tx);
|
||||||
|
if (doc) return doc;
|
||||||
|
|
||||||
|
const { privateKey, publicKey } = generateAsymmetricKeyPair();
|
||||||
|
const key = generateSymmetricKey();
|
||||||
|
const {
|
||||||
|
ciphertext: encryptedPrivateKey,
|
||||||
|
iv: privateKeyIV,
|
||||||
|
tag: privateKeyTag,
|
||||||
|
encoding: privateKeyKeyEncoding,
|
||||||
|
algorithm: privateKeyAlgorithm
|
||||||
|
} = infisicalSymmetricEncypt(privateKey);
|
||||||
|
const {
|
||||||
|
ciphertext: encryptedSymmetricKey,
|
||||||
|
iv: symmetricKeyIV,
|
||||||
|
tag: symmetricKeyTag,
|
||||||
|
encoding: symmetricKeyKeyEncoding,
|
||||||
|
algorithm: symmetricKeyAlgorithm
|
||||||
|
} = infisicalSymmetricEncypt(key);
|
||||||
|
|
||||||
|
return orgBotDAL.create(
|
||||||
|
{
|
||||||
|
name: "Infisical org bot",
|
||||||
|
publicKey,
|
||||||
|
privateKeyIV,
|
||||||
|
encryptedPrivateKey,
|
||||||
|
symmetricKeyIV,
|
||||||
|
symmetricKeyTag,
|
||||||
|
encryptedSymmetricKey,
|
||||||
|
symmetricKeyAlgorithm,
|
||||||
|
orgId: identityMembershipOrg.orgId,
|
||||||
|
privateKeyTag,
|
||||||
|
privateKeyAlgorithm,
|
||||||
|
privateKeyKeyEncoding,
|
||||||
|
symmetricKeyKeyEncoding
|
||||||
|
},
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const key = infisicalSymmetricDecrypt({
|
||||||
|
ciphertext: orgBot.encryptedSymmetricKey,
|
||||||
|
iv: orgBot.symmetricKeyIV,
|
||||||
|
tag: orgBot.symmetricKeyTag,
|
||||||
|
keyEncoding: orgBot.symmetricKeyKeyEncoding as SecretKeyEncoding
|
||||||
|
});
|
||||||
|
|
||||||
|
const { ciphertext: encryptedCaCert, iv: caCertIV, tag: caCertTag } = encryptSymmetric(caCert, key);
|
||||||
|
|
||||||
|
const identityOidcAuth = await identityOidcAuthDAL.transaction(async (tx) => {
|
||||||
|
const doc = await identityOidcAuthDAL.create(
|
||||||
|
{
|
||||||
|
identityId: identityMembershipOrg.identityId,
|
||||||
|
oidcDiscoveryUrl,
|
||||||
|
encryptedCaCert,
|
||||||
|
caCertIV,
|
||||||
|
caCertTag,
|
||||||
|
boundIssuer,
|
||||||
|
boundAudiences,
|
||||||
|
boundClaims,
|
||||||
|
boundSubject,
|
||||||
|
accessTokenMaxTTL,
|
||||||
|
accessTokenTTL,
|
||||||
|
accessTokenNumUsesLimit,
|
||||||
|
accessTokenTrustedIps: JSON.stringify(reformattedAccessTokenTrustedIps)
|
||||||
|
},
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
await identityDAL.updateById(
|
||||||
|
identityMembershipOrg.identityId,
|
||||||
|
{
|
||||||
|
authMethod: IdentityAuthMethod.OIDC_AUTH
|
||||||
|
},
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
return doc;
|
||||||
|
});
|
||||||
|
return { ...identityOidcAuth, orgId: identityMembershipOrg.orgId, caCert };
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateOidcAuth = async ({
|
||||||
|
identityId,
|
||||||
|
oidcDiscoveryUrl,
|
||||||
|
caCert,
|
||||||
|
boundIssuer,
|
||||||
|
boundAudiences,
|
||||||
|
boundClaims,
|
||||||
|
boundSubject,
|
||||||
|
accessTokenTTL,
|
||||||
|
accessTokenMaxTTL,
|
||||||
|
accessTokenNumUsesLimit,
|
||||||
|
accessTokenTrustedIps,
|
||||||
|
actorId,
|
||||||
|
actorAuthMethod,
|
||||||
|
actor,
|
||||||
|
actorOrgId
|
||||||
|
}: TUpdateOidcAuthDTO) => {
|
||||||
|
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: "Failed to update OIDC Auth"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const identityOidcAuth = await identityOidcAuthDAL.findOne({ identityId });
|
||||||
|
|
||||||
|
if (
|
||||||
|
(accessTokenMaxTTL || identityOidcAuth.accessTokenMaxTTL) > 0 &&
|
||||||
|
(accessTokenTTL || identityOidcAuth.accessTokenMaxTTL) > (accessTokenMaxTTL || identityOidcAuth.accessTokenMaxTTL)
|
||||||
|
) {
|
||||||
|
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
||||||
|
}
|
||||||
|
|
||||||
|
const { permission } = await permissionService.getOrgPermission(
|
||||||
|
actor,
|
||||||
|
actorId,
|
||||||
|
identityMembershipOrg.orgId,
|
||||||
|
actorAuthMethod,
|
||||||
|
actorOrgId
|
||||||
|
);
|
||||||
|
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Identity);
|
||||||
|
|
||||||
|
const plan = await licenseService.getPlan(identityMembershipOrg.orgId);
|
||||||
|
const reformattedAccessTokenTrustedIps = accessTokenTrustedIps?.map((accessTokenTrustedIp) => {
|
||||||
|
if (
|
||||||
|
!plan.ipAllowlisting &&
|
||||||
|
accessTokenTrustedIp.ipAddress !== "0.0.0.0/0" &&
|
||||||
|
accessTokenTrustedIp.ipAddress !== "::/0"
|
||||||
|
)
|
||||||
|
throw new BadRequestError({
|
||||||
|
message:
|
||||||
|
"Failed to add IP access range to access token due to plan restriction. Upgrade plan to add IP access range."
|
||||||
|
});
|
||||||
|
if (!isValidIpOrCidr(accessTokenTrustedIp.ipAddress))
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "The IP is not a valid IPv4, IPv6, or CIDR block"
|
||||||
|
});
|
||||||
|
return extractIPDetails(accessTokenTrustedIp.ipAddress);
|
||||||
|
});
|
||||||
|
|
||||||
|
const updateQuery: TIdentityOidcAuthsUpdate = {
|
||||||
|
oidcDiscoveryUrl,
|
||||||
|
boundIssuer,
|
||||||
|
boundAudiences,
|
||||||
|
boundClaims,
|
||||||
|
boundSubject,
|
||||||
|
accessTokenMaxTTL,
|
||||||
|
accessTokenTTL,
|
||||||
|
accessTokenNumUsesLimit,
|
||||||
|
accessTokenTrustedIps: reformattedAccessTokenTrustedIps
|
||||||
|
? JSON.stringify(reformattedAccessTokenTrustedIps)
|
||||||
|
: undefined
|
||||||
|
};
|
||||||
|
|
||||||
|
const orgBot = await orgBotDAL.findOne({ orgId: identityMembershipOrg.orgId });
|
||||||
|
if (!orgBot) {
|
||||||
|
throw new BadRequestError({ message: "Org bot not found", name: "OrgBotNotFound" });
|
||||||
|
}
|
||||||
|
|
||||||
|
const key = infisicalSymmetricDecrypt({
|
||||||
|
ciphertext: orgBot.encryptedSymmetricKey,
|
||||||
|
iv: orgBot.symmetricKeyIV,
|
||||||
|
tag: orgBot.symmetricKeyTag,
|
||||||
|
keyEncoding: orgBot.symmetricKeyKeyEncoding as SecretKeyEncoding
|
||||||
|
});
|
||||||
|
|
||||||
|
if (caCert !== undefined) {
|
||||||
|
const { ciphertext: encryptedCACert, iv: caCertIV, tag: caCertTag } = encryptSymmetric(caCert, key);
|
||||||
|
updateQuery.encryptedCaCert = encryptedCACert;
|
||||||
|
updateQuery.caCertIV = caCertIV;
|
||||||
|
updateQuery.caCertTag = caCertTag;
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedOidcAuth = await identityOidcAuthDAL.updateById(identityOidcAuth.id, updateQuery);
|
||||||
|
const updatedCACert =
|
||||||
|
updatedOidcAuth.encryptedCaCert && updatedOidcAuth.caCertIV && updatedOidcAuth.caCertTag
|
||||||
|
? decryptSymmetric({
|
||||||
|
ciphertext: updatedOidcAuth.encryptedCaCert,
|
||||||
|
iv: updatedOidcAuth.caCertIV,
|
||||||
|
tag: updatedOidcAuth.caCertTag,
|
||||||
|
key
|
||||||
|
})
|
||||||
|
: "";
|
||||||
|
|
||||||
|
return {
|
||||||
|
...updatedOidcAuth,
|
||||||
|
orgId: identityMembershipOrg.orgId,
|
||||||
|
caCert: updatedCACert
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const getOidcAuth = async ({ identityId, actorId, actor, actorAuthMethod, actorOrgId }: TGetOidcAuthDTO) => {
|
||||||
|
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 attached"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const { permission } = await permissionService.getOrgPermission(
|
||||||
|
actor,
|
||||||
|
actorId,
|
||||||
|
identityMembershipOrg.orgId,
|
||||||
|
actorAuthMethod,
|
||||||
|
actorOrgId
|
||||||
|
);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Identity);
|
||||||
|
|
||||||
|
const identityOidcAuth = await identityOidcAuthDAL.findOne({ identityId });
|
||||||
|
|
||||||
|
const orgBot = await orgBotDAL.findOne({ orgId: identityMembershipOrg.orgId });
|
||||||
|
if (!orgBot) {
|
||||||
|
throw new BadRequestError({ message: "Org bot not found", name: "OrgBotNotFound" });
|
||||||
|
}
|
||||||
|
|
||||||
|
const key = infisicalSymmetricDecrypt({
|
||||||
|
ciphertext: orgBot.encryptedSymmetricKey,
|
||||||
|
iv: orgBot.symmetricKeyIV,
|
||||||
|
tag: orgBot.symmetricKeyTag,
|
||||||
|
keyEncoding: orgBot.symmetricKeyKeyEncoding as SecretKeyEncoding
|
||||||
|
});
|
||||||
|
|
||||||
|
const caCert = decryptSymmetric({
|
||||||
|
ciphertext: identityOidcAuth.encryptedCaCert,
|
||||||
|
iv: identityOidcAuth.caCertIV,
|
||||||
|
tag: identityOidcAuth.caCertTag,
|
||||||
|
key
|
||||||
|
});
|
||||||
|
|
||||||
|
return { ...identityOidcAuth, orgId: identityMembershipOrg.orgId, caCert };
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
attachOidcAuth,
|
||||||
|
updateOidcAuth,
|
||||||
|
getOidcAuth
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import { TProjectPermission } from "@app/lib/types";
|
||||||
|
|
||||||
|
export type TAttachOidcAuthDTO = {
|
||||||
|
identityId: string;
|
||||||
|
oidcDiscoveryUrl: string;
|
||||||
|
caCert: string;
|
||||||
|
boundIssuer: string;
|
||||||
|
boundAudiences: string;
|
||||||
|
boundClaims: Record<string, string>;
|
||||||
|
boundSubject: string;
|
||||||
|
accessTokenTTL: number;
|
||||||
|
accessTokenMaxTTL: number;
|
||||||
|
accessTokenNumUsesLimit: number;
|
||||||
|
accessTokenTrustedIps: { ipAddress: string }[];
|
||||||
|
} & Omit<TProjectPermission, "projectId">;
|
||||||
|
|
||||||
|
export type TUpdateOidcAuthDTO = {
|
||||||
|
identityId: string;
|
||||||
|
oidcDiscoveryUrl?: string;
|
||||||
|
caCert?: string;
|
||||||
|
boundIssuer?: string;
|
||||||
|
boundAudiences?: string;
|
||||||
|
boundClaims?: Record<string, string>;
|
||||||
|
boundSubject?: string;
|
||||||
|
accessTokenTTL?: number;
|
||||||
|
accessTokenMaxTTL?: number;
|
||||||
|
accessTokenNumUsesLimit?: number;
|
||||||
|
accessTokenTrustedIps?: { ipAddress: string }[];
|
||||||
|
} & Omit<TProjectPermission, "projectId">;
|
||||||
|
|
||||||
|
export type TGetOidcAuthDTO = {
|
||||||
|
identityId: string;
|
||||||
|
} & Omit<TProjectPermission, "projectId">;
|
||||||
|
|||||||
@@ -5,5 +5,6 @@ export const identityAuthToNameMap: { [I in IdentityAuthMethod]: string } = {
|
|||||||
[IdentityAuthMethod.KUBERNETES_AUTH]: "Kubernetes Auth",
|
[IdentityAuthMethod.KUBERNETES_AUTH]: "Kubernetes Auth",
|
||||||
[IdentityAuthMethod.GCP_AUTH]: "GCP Auth",
|
[IdentityAuthMethod.GCP_AUTH]: "GCP Auth",
|
||||||
[IdentityAuthMethod.AWS_AUTH]: "AWS Auth",
|
[IdentityAuthMethod.AWS_AUTH]: "AWS Auth",
|
||||||
[IdentityAuthMethod.AZURE_AUTH]: "Azure Auth"
|
[IdentityAuthMethod.AZURE_AUTH]: "Azure Auth",
|
||||||
|
[IdentityAuthMethod.OIDC_AUTH]: "OIDC Auth"
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
AddIdentityAzureAuthDTO,
|
AddIdentityAzureAuthDTO,
|
||||||
AddIdentityGcpAuthDTO,
|
AddIdentityGcpAuthDTO,
|
||||||
AddIdentityKubernetesAuthDTO,
|
AddIdentityKubernetesAuthDTO,
|
||||||
|
AddIdentityOidcAuthDTO,
|
||||||
AddIdentityUniversalAuthDTO,
|
AddIdentityUniversalAuthDTO,
|
||||||
ClientSecretData,
|
ClientSecretData,
|
||||||
CreateIdentityDTO,
|
CreateIdentityDTO,
|
||||||
@@ -21,12 +22,14 @@ import {
|
|||||||
IdentityAzureAuth,
|
IdentityAzureAuth,
|
||||||
IdentityGcpAuth,
|
IdentityGcpAuth,
|
||||||
IdentityKubernetesAuth,
|
IdentityKubernetesAuth,
|
||||||
|
IdentityOidcAuth,
|
||||||
IdentityUniversalAuth,
|
IdentityUniversalAuth,
|
||||||
UpdateIdentityAwsAuthDTO,
|
UpdateIdentityAwsAuthDTO,
|
||||||
UpdateIdentityAzureAuthDTO,
|
UpdateIdentityAzureAuthDTO,
|
||||||
UpdateIdentityDTO,
|
UpdateIdentityDTO,
|
||||||
UpdateIdentityGcpAuthDTO,
|
UpdateIdentityGcpAuthDTO,
|
||||||
UpdateIdentityKubernetesAuthDTO,
|
UpdateIdentityKubernetesAuthDTO,
|
||||||
|
UpdateIdentityOidcAuthDTO,
|
||||||
UpdateIdentityUniversalAuthDTO
|
UpdateIdentityUniversalAuthDTO
|
||||||
} from "./types";
|
} from "./types";
|
||||||
|
|
||||||
@@ -330,6 +333,90 @@ export const useUpdateIdentityAwsAuth = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useUpdateIdentityOidcAuth = () => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
return useMutation<IdentityOidcAuth, {}, UpdateIdentityOidcAuthDTO>({
|
||||||
|
mutationFn: async ({
|
||||||
|
identityId,
|
||||||
|
accessTokenTTL,
|
||||||
|
accessTokenMaxTTL,
|
||||||
|
accessTokenNumUsesLimit,
|
||||||
|
accessTokenTrustedIps,
|
||||||
|
oidcDiscoveryUrl,
|
||||||
|
caCert,
|
||||||
|
boundIssuer,
|
||||||
|
boundAudiences,
|
||||||
|
boundClaims,
|
||||||
|
boundSubject
|
||||||
|
}) => {
|
||||||
|
const {
|
||||||
|
data: { identityOidcAuth }
|
||||||
|
} = await apiRequest.patch<{ identityOidcAuth: IdentityOidcAuth }>(
|
||||||
|
`/api/v1/auth/oidc-auth/identities/${identityId}`,
|
||||||
|
{
|
||||||
|
oidcDiscoveryUrl,
|
||||||
|
caCert,
|
||||||
|
boundIssuer,
|
||||||
|
boundAudiences,
|
||||||
|
boundClaims,
|
||||||
|
boundSubject,
|
||||||
|
accessTokenTTL,
|
||||||
|
accessTokenMaxTTL,
|
||||||
|
accessTokenNumUsesLimit,
|
||||||
|
accessTokenTrustedIps
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return identityOidcAuth;
|
||||||
|
},
|
||||||
|
onSuccess: (_, { organizationId }) => {
|
||||||
|
queryClient.invalidateQueries(organizationKeys.getOrgIdentityMemberships(organizationId));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useAddIdentityOidcAuth = () => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
return useMutation<IdentityOidcAuth, {}, AddIdentityOidcAuthDTO>({
|
||||||
|
mutationFn: async ({
|
||||||
|
identityId,
|
||||||
|
oidcDiscoveryUrl,
|
||||||
|
caCert,
|
||||||
|
boundIssuer,
|
||||||
|
boundAudiences,
|
||||||
|
boundClaims,
|
||||||
|
boundSubject,
|
||||||
|
accessTokenTTL,
|
||||||
|
accessTokenMaxTTL,
|
||||||
|
accessTokenNumUsesLimit,
|
||||||
|
accessTokenTrustedIps
|
||||||
|
}) => {
|
||||||
|
const {
|
||||||
|
data: { identityOidcAuth }
|
||||||
|
} = await apiRequest.post<{ identityOidcAuth: IdentityOidcAuth }>(
|
||||||
|
`/api/v1/auth/oidc-auth/identities/${identityId}`,
|
||||||
|
{
|
||||||
|
oidcDiscoveryUrl,
|
||||||
|
caCert,
|
||||||
|
boundIssuer,
|
||||||
|
boundAudiences,
|
||||||
|
boundClaims,
|
||||||
|
boundSubject,
|
||||||
|
accessTokenTTL,
|
||||||
|
accessTokenMaxTTL,
|
||||||
|
accessTokenNumUsesLimit,
|
||||||
|
accessTokenTrustedIps
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return identityOidcAuth;
|
||||||
|
},
|
||||||
|
onSuccess: (_, { organizationId }) => {
|
||||||
|
queryClient.invalidateQueries(organizationKeys.getOrgIdentityMemberships(organizationId));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export const useAddIdentityAzureAuth = () => {
|
export const useAddIdentityAzureAuth = () => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
return useMutation<IdentityAzureAuth, {}, AddIdentityAzureAuthDTO>({
|
return useMutation<IdentityAzureAuth, {}, AddIdentityAzureAuthDTO>({
|
||||||
|
|||||||
@@ -8,7 +8,9 @@ import {
|
|||||||
IdentityAzureAuth,
|
IdentityAzureAuth,
|
||||||
IdentityGcpAuth,
|
IdentityGcpAuth,
|
||||||
IdentityKubernetesAuth,
|
IdentityKubernetesAuth,
|
||||||
IdentityUniversalAuth} from "./types";
|
IdentityOidcAuth,
|
||||||
|
IdentityUniversalAuth
|
||||||
|
} from "./types";
|
||||||
|
|
||||||
export const identitiesKeys = {
|
export const identitiesKeys = {
|
||||||
getIdentityUniversalAuth: (identityId: string) =>
|
getIdentityUniversalAuth: (identityId: string) =>
|
||||||
@@ -18,6 +20,7 @@ export const identitiesKeys = {
|
|||||||
getIdentityKubernetesAuth: (identityId: string) =>
|
getIdentityKubernetesAuth: (identityId: string) =>
|
||||||
[{ identityId }, "identity-kubernetes-auth"] as const,
|
[{ identityId }, "identity-kubernetes-auth"] as const,
|
||||||
getIdentityGcpAuth: (identityId: string) => [{ identityId }, "identity-gcp-auth"] as const,
|
getIdentityGcpAuth: (identityId: string) => [{ identityId }, "identity-gcp-auth"] as const,
|
||||||
|
getIdentityOidcAuth: (identityId: string) => [{ identityId }, "identity-oidc-auth"] as const,
|
||||||
getIdentityAwsAuth: (identityId: string) => [{ identityId }, "identity-aws-auth"] as const,
|
getIdentityAwsAuth: (identityId: string) => [{ identityId }, "identity-aws-auth"] as const,
|
||||||
getIdentityAzureAuth: (identityId: string) => [{ identityId }, "identity-azure-auth"] as const
|
getIdentityAzureAuth: (identityId: string) => [{ identityId }, "identity-azure-auth"] as const
|
||||||
};
|
};
|
||||||
@@ -111,3 +114,18 @@ export const useGetIdentityKubernetesAuth = (identityId: string) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useGetIdentityOidcAuth = (identityId: string) => {
|
||||||
|
return useQuery({
|
||||||
|
enabled: Boolean(identityId),
|
||||||
|
queryKey: identitiesKeys.getIdentityOidcAuth(identityId),
|
||||||
|
queryFn: async () => {
|
||||||
|
const {
|
||||||
|
data: { identityOidcAuth }
|
||||||
|
} = await apiRequest.get<{ identityOidcAuth: IdentityOidcAuth }>(
|
||||||
|
`/api/v1/auth/oidc-auth/identities/${identityId}`
|
||||||
|
);
|
||||||
|
return identityOidcAuth;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
@@ -155,6 +155,54 @@ export type UpdateIdentityGcpAuthDTO = {
|
|||||||
}[];
|
}[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type IdentityOidcAuth = {
|
||||||
|
identityId: string;
|
||||||
|
oidcDiscoveryUrl: string;
|
||||||
|
caCert: string;
|
||||||
|
boundIssuer: string;
|
||||||
|
boundAudiences: string;
|
||||||
|
boundClaims: Record<string, string>;
|
||||||
|
boundSubject: string;
|
||||||
|
accessTokenTTL: number;
|
||||||
|
accessTokenMaxTTL: number;
|
||||||
|
accessTokenNumUsesLimit: number;
|
||||||
|
accessTokenTrustedIps: IdentityTrustedIp[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type AddIdentityOidcAuthDTO = {
|
||||||
|
organizationId: string;
|
||||||
|
identityId: string;
|
||||||
|
oidcDiscoveryUrl: string;
|
||||||
|
caCert: string;
|
||||||
|
boundIssuer: string;
|
||||||
|
boundAudiences: string;
|
||||||
|
boundClaims: Record<string, string>;
|
||||||
|
boundSubject: string;
|
||||||
|
accessTokenTTL: number;
|
||||||
|
accessTokenMaxTTL: number;
|
||||||
|
accessTokenNumUsesLimit: number;
|
||||||
|
accessTokenTrustedIps: {
|
||||||
|
ipAddress: string;
|
||||||
|
}[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type UpdateIdentityOidcAuthDTO = {
|
||||||
|
organizationId: string;
|
||||||
|
identityId: string;
|
||||||
|
oidcDiscoveryUrl?: string;
|
||||||
|
caCert?: string;
|
||||||
|
boundIssuer?: string;
|
||||||
|
boundAudiences?: string;
|
||||||
|
boundClaims?: Record<string, string>;
|
||||||
|
boundSubject?: string;
|
||||||
|
accessTokenTTL?: number;
|
||||||
|
accessTokenMaxTTL?: number;
|
||||||
|
accessTokenNumUsesLimit?: number;
|
||||||
|
accessTokenTrustedIps?: {
|
||||||
|
ipAddress: string;
|
||||||
|
}[];
|
||||||
|
};
|
||||||
|
|
||||||
export type IdentityAwsAuth = {
|
export type IdentityAwsAuth = {
|
||||||
identityId: string;
|
identityId: string;
|
||||||
type: "iam";
|
type: "iam";
|
||||||
|
|||||||
+99
-74
@@ -6,9 +6,14 @@ import { zodResolver } from "@hookform/resolvers/zod";
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { createNotification } from "@app/components/notifications";
|
import { createNotification } from "@app/components/notifications";
|
||||||
import { Button, FormControl, IconButton, Input } from "@app/components/v2";
|
import { Button, FormControl, IconButton, Input, TextArea } from "@app/components/v2";
|
||||||
import { useOrganization, useSubscription } from "@app/context";
|
import { useOrganization, useSubscription } from "@app/context";
|
||||||
import { IdentityAuthMethod } from "@app/hooks/api/identities";
|
import { IdentityAuthMethod } from "@app/hooks/api/identities";
|
||||||
|
import {
|
||||||
|
useAddIdentityOidcAuth,
|
||||||
|
useUpdateIdentityOidcAuth
|
||||||
|
} from "@app/hooks/api/identities/mutations";
|
||||||
|
import { useGetIdentityOidcAuth } from "@app/hooks/api/identities/queries";
|
||||||
import { IdentityTrustedIp } from "@app/hooks/api/identities/types";
|
import { IdentityTrustedIp } from "@app/hooks/api/identities/types";
|
||||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||||
|
|
||||||
@@ -60,10 +65,10 @@ export const IdentityOidcAuthForm = ({
|
|||||||
const orgId = currentOrg?.id || "";
|
const orgId = currentOrg?.id || "";
|
||||||
const { subscription } = useSubscription();
|
const { subscription } = useSubscription();
|
||||||
|
|
||||||
// const { mutateAsync: addMutateAsync } = useAddIdentityGcpAuth();
|
const { mutateAsync: addMutateAsync } = useAddIdentityOidcAuth();
|
||||||
// const { mutateAsync: updateMutateAsync } = useUpdateIdentityGcpAuth();
|
const { mutateAsync: updateMutateAsync } = useUpdateIdentityOidcAuth();
|
||||||
|
|
||||||
// const { data } = useGetIdentityGcpAuth(identityAuthMethodData?.identityId ?? "");
|
const { data } = useGetIdentityOidcAuth(identityAuthMethodData?.identityId ?? "");
|
||||||
|
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
@@ -95,78 +100,93 @@ export const IdentityOidcAuthForm = ({
|
|||||||
remove: removeAccessTokenTrustedIp
|
remove: removeAccessTokenTrustedIp
|
||||||
} = useFieldArray({ control, name: "accessTokenTrustedIps" });
|
} = useFieldArray({ control, name: "accessTokenTrustedIps" });
|
||||||
|
|
||||||
// useEffect(() => {
|
useEffect(() => {
|
||||||
// if (data) {
|
if (data) {
|
||||||
// reset({
|
reset({
|
||||||
// type: data.type,
|
oidcDiscoveryUrl: data.oidcDiscoveryUrl,
|
||||||
// allowedServiceAccounts: data.allowedServiceAccounts,
|
caCert: data.caCert,
|
||||||
// allowedProjects: data.allowedProjects,
|
boundIssuer: data.boundIssuer,
|
||||||
// allowedZones: data.allowedZones,
|
boundAudiences: data.boundAudiences,
|
||||||
// accessTokenTTL: String(data.accessTokenTTL),
|
boundClaims: Object.entries(data.boundClaims).map(([key, value]) => ({
|
||||||
// accessTokenMaxTTL: String(data.accessTokenMaxTTL),
|
key,
|
||||||
// accessTokenNumUsesLimit: String(data.accessTokenNumUsesLimit),
|
value
|
||||||
// accessTokenTrustedIps: data.accessTokenTrustedIps.map(
|
})),
|
||||||
// ({ ipAddress, prefix }: IdentityTrustedIp) => {
|
boundSubject: data.boundSubject,
|
||||||
// return {
|
accessTokenTTL: String(data.accessTokenTTL),
|
||||||
// ipAddress: `${ipAddress}${prefix !== undefined ? `/${prefix}` : ""}`
|
accessTokenMaxTTL: String(data.accessTokenMaxTTL),
|
||||||
// };
|
accessTokenNumUsesLimit: String(data.accessTokenNumUsesLimit),
|
||||||
// }
|
accessTokenTrustedIps: data.accessTokenTrustedIps.map(
|
||||||
// )
|
({ ipAddress, prefix }: IdentityTrustedIp) => {
|
||||||
// });
|
return {
|
||||||
// } else {
|
ipAddress: `${ipAddress}${prefix !== undefined ? `/${prefix}` : ""}`
|
||||||
// reset({
|
};
|
||||||
// type: "iam",
|
}
|
||||||
// allowedServiceAccounts: "",
|
)
|
||||||
// allowedProjects: "",
|
});
|
||||||
// allowedZones: "",
|
} else {
|
||||||
// accessTokenTTL: "2592000",
|
reset({
|
||||||
// accessTokenMaxTTL: "2592000",
|
oidcDiscoveryUrl: "",
|
||||||
// accessTokenNumUsesLimit: "0",
|
caCert: "",
|
||||||
// accessTokenTrustedIps: [{ ipAddress: "0.0.0.0/0" }, { ipAddress: "::/0" }]
|
boundIssuer: "",
|
||||||
// });
|
boundAudiences: "",
|
||||||
// }
|
boundClaims: [],
|
||||||
// }, [data]);
|
boundSubject: "",
|
||||||
|
accessTokenTTL: "2592000",
|
||||||
|
accessTokenMaxTTL: "2592000",
|
||||||
|
accessTokenNumUsesLimit: "0",
|
||||||
|
accessTokenTrustedIps: [{ ipAddress: "0.0.0.0/0" }, { ipAddress: "::/0" }]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [data]);
|
||||||
|
|
||||||
const onFormSubmit = async ({
|
const onFormSubmit = async ({
|
||||||
type,
|
accessTokenTrustedIps,
|
||||||
allowedServiceAccounts,
|
|
||||||
allowedProjects,
|
|
||||||
allowedZones,
|
|
||||||
accessTokenTTL,
|
accessTokenTTL,
|
||||||
accessTokenMaxTTL,
|
accessTokenMaxTTL,
|
||||||
accessTokenNumUsesLimit,
|
accessTokenNumUsesLimit,
|
||||||
accessTokenTrustedIps
|
oidcDiscoveryUrl,
|
||||||
|
caCert,
|
||||||
|
boundIssuer,
|
||||||
|
boundAudiences,
|
||||||
|
boundClaims,
|
||||||
|
boundSubject
|
||||||
}: FormData) => {
|
}: FormData) => {
|
||||||
try {
|
try {
|
||||||
if (!identityAuthMethodData) return;
|
if (!identityAuthMethodData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// if (data) {
|
if (data) {
|
||||||
// await updateMutateAsync({
|
await updateMutateAsync({
|
||||||
// identityId: identityAuthMethodData.identityId,
|
identityId: identityAuthMethodData.identityId,
|
||||||
// organizationId: orgId,
|
organizationId: orgId,
|
||||||
// type,
|
oidcDiscoveryUrl,
|
||||||
// allowedServiceAccounts,
|
caCert,
|
||||||
// allowedProjects,
|
boundIssuer,
|
||||||
// allowedZones,
|
boundAudiences,
|
||||||
// accessTokenTTL: Number(accessTokenTTL),
|
boundClaims: Object.fromEntries(boundClaims.map((entry) => [entry.key, entry.value])),
|
||||||
// accessTokenMaxTTL: Number(accessTokenMaxTTL),
|
boundSubject,
|
||||||
// accessTokenNumUsesLimit: Number(accessTokenNumUsesLimit),
|
accessTokenTTL: Number(accessTokenTTL),
|
||||||
// accessTokenTrustedIps
|
accessTokenMaxTTL: Number(accessTokenMaxTTL),
|
||||||
// });
|
accessTokenNumUsesLimit: Number(accessTokenNumUsesLimit),
|
||||||
// } else {
|
accessTokenTrustedIps
|
||||||
// await addMutateAsync({
|
});
|
||||||
// identityId: identityAuthMethodData.identityId,
|
} else {
|
||||||
// organizationId: orgId,
|
await addMutateAsync({
|
||||||
// type,
|
identityId: identityAuthMethodData.identityId,
|
||||||
// allowedServiceAccounts: allowedServiceAccounts || "",
|
oidcDiscoveryUrl,
|
||||||
// allowedProjects: allowedProjects || "",
|
caCert,
|
||||||
// allowedZones: allowedZones || "",
|
boundIssuer,
|
||||||
// accessTokenTTL: Number(accessTokenTTL),
|
boundAudiences,
|
||||||
// accessTokenMaxTTL: Number(accessTokenMaxTTL),
|
boundClaims: Object.fromEntries(boundClaims.map((entry) => [entry.key, entry.value])),
|
||||||
// accessTokenNumUsesLimit: Number(accessTokenNumUsesLimit),
|
boundSubject,
|
||||||
// accessTokenTrustedIps
|
organizationId: orgId,
|
||||||
// });
|
accessTokenTTL: Number(accessTokenTTL),
|
||||||
// }
|
accessTokenMaxTTL: Number(accessTokenMaxTTL),
|
||||||
|
accessTokenNumUsesLimit: Number(accessTokenNumUsesLimit),
|
||||||
|
accessTokenTrustedIps
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
handlePopUpToggle("identityAuthMethod", false);
|
handlePopUpToggle("identityAuthMethod", false);
|
||||||
|
|
||||||
@@ -198,11 +218,7 @@ export const IdentityOidcAuthForm = ({
|
|||||||
isError={Boolean(error)}
|
isError={Boolean(error)}
|
||||||
errorText={error?.message}
|
errorText={error?.message}
|
||||||
>
|
>
|
||||||
<Input
|
<Input {...field} placeholder="https://accounts.google.com" type="text" />
|
||||||
{...field}
|
|
||||||
placeholder="https://accounts.google.com/.well-known/openid-configuration"
|
|
||||||
type="text"
|
|
||||||
/>
|
|
||||||
</FormControl>
|
</FormControl>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
@@ -216,7 +232,16 @@ export const IdentityOidcAuthForm = ({
|
|||||||
isError={Boolean(error)}
|
isError={Boolean(error)}
|
||||||
errorText={error?.message}
|
errorText={error?.message}
|
||||||
>
|
>
|
||||||
<Input {...field} type="text" />
|
<Input {...field} type="text" placeholder="https://accounts.google.com" />
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="caCert"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl label="CA Certificate" errorText={error?.message} isError={Boolean(error)}>
|
||||||
|
<TextArea {...field} placeholder="-----BEGIN CERTIFICATE----- ..." />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
Reference in New Issue
Block a user