mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
misc: addressed type issue and ux improvements
This commit is contained in:
@@ -67,6 +67,8 @@ export async function up(knex: Knex): Promise<void> {
|
||||
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
||||
t.string("type").notNullable();
|
||||
t.string("name").notNullable();
|
||||
t.string("projectId").notNullable();
|
||||
t.foreign("projectId").references("id").inTable(TableName.Project).onDelete("CASCADE");
|
||||
t.uuid("appConnectionId").nullable();
|
||||
t.foreign("appConnectionId").references("id").inTable(TableName.AppConnection);
|
||||
t.uuid("dnsAppConnectionId").nullable();
|
||||
@@ -79,6 +81,13 @@ export async function up(knex: Knex): Promise<void> {
|
||||
t.binary("credentials");
|
||||
t.json("configuration");
|
||||
t.string("status").notNullable();
|
||||
t.unique(["projectId", "name"]);
|
||||
});
|
||||
}
|
||||
|
||||
if (await knex.schema.hasTable(TableName.PkiSubscriber)) {
|
||||
await knex.schema.alterTable(TableName.PkiSubscriber, (t) => {
|
||||
t.string("ttl").nullable().alter();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,8 +25,8 @@ export const CertificatesSchema = z.object({
|
||||
certificateTemplateId: z.string().uuid().nullable().optional(),
|
||||
keyUsages: z.string().array().nullable().optional(),
|
||||
extendedKeyUsages: z.string().array().nullable().optional(),
|
||||
projectId: z.string(),
|
||||
pkiSubscriberId: z.string().uuid().nullable().optional()
|
||||
pkiSubscriberId: z.string().uuid().nullable().optional(),
|
||||
projectId: z.string()
|
||||
});
|
||||
|
||||
export type TCertificates = z.infer<typeof CertificatesSchema>;
|
||||
|
||||
@@ -13,6 +13,7 @@ export const ExternalCertificateAuthoritiesSchema = z.object({
|
||||
id: z.string().uuid(),
|
||||
type: z.string(),
|
||||
name: z.string(),
|
||||
projectId: z.string(),
|
||||
appConnectionId: z.string().uuid().nullable().optional(),
|
||||
dnsAppConnectionId: z.string().uuid().nullable().optional(),
|
||||
certificateAuthorityId: z.string().uuid(),
|
||||
|
||||
@@ -16,7 +16,7 @@ export const PkiSubscribersSchema = z.object({
|
||||
name: z.string(),
|
||||
commonName: z.string(),
|
||||
subjectAlternativeNames: z.string().array(),
|
||||
ttl: z.string(),
|
||||
ttl: z.string().nullable().optional(),
|
||||
keyUsages: z.string().array(),
|
||||
extendedKeyUsages: z.string().array(),
|
||||
status: z.string()
|
||||
|
||||
@@ -1997,7 +1997,7 @@ interface CreatePkiSubscriber {
|
||||
caId?: string;
|
||||
name: string;
|
||||
commonName: string;
|
||||
ttl: string;
|
||||
ttl?: string;
|
||||
subjectAlternativeNames: string[];
|
||||
keyUsages: CertKeyUsage[];
|
||||
extendedKeyUsages: CertExtendedKeyUsage[];
|
||||
|
||||
@@ -6,15 +6,9 @@ import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
import { CaStatus, CaType } from "@app/services/certificate-authority/certificate-authority-enums";
|
||||
import {
|
||||
TCertificateAuthority,
|
||||
TCertificateAuthorityInput
|
||||
} from "@app/services/certificate-authority/certificate-authority-types";
|
||||
import { TCertificateAuthority } from "@app/services/certificate-authority/certificate-authority-types";
|
||||
|
||||
export const registerCertificateAuthorityEndpoints = <
|
||||
T extends TCertificateAuthority,
|
||||
I extends TCertificateAuthorityInput
|
||||
>({
|
||||
export const registerCertificateAuthorityEndpoints = <T extends TCertificateAuthority>({
|
||||
server,
|
||||
caType,
|
||||
createSchema,
|
||||
@@ -27,13 +21,13 @@ export const registerCertificateAuthorityEndpoints = <
|
||||
name: string;
|
||||
projectId: string;
|
||||
status: CaStatus;
|
||||
configuration: I["configuration"];
|
||||
configuration: T["configuration"];
|
||||
disableDirectIssuance: boolean;
|
||||
}>;
|
||||
updateSchema: z.ZodType<{
|
||||
name?: string;
|
||||
status?: CaStatus;
|
||||
configuration?: I["configuration"];
|
||||
configuration?: T["configuration"];
|
||||
disableDirectIssuance?: boolean;
|
||||
}>;
|
||||
responseSchema: z.ZodTypeAny;
|
||||
|
||||
@@ -90,7 +90,8 @@ export const registerPkiSubscriberRouter = async (server: FastifyZodProvider) =>
|
||||
ttl: z
|
||||
.string()
|
||||
.trim()
|
||||
.refine((val) => ms(val) > 0, "TTL must be a positive number")
|
||||
.refine((val) => !val || ms(val) > 0, "TTL must be a positive number")
|
||||
.optional()
|
||||
.describe(PKI_SUBSCRIBERS.CREATE.ttl),
|
||||
subjectAlternativeNames: validateAltNameField
|
||||
.array()
|
||||
@@ -134,7 +135,7 @@ export const registerPkiSubscriberRouter = async (server: FastifyZodProvider) =>
|
||||
caId: subscriber.caId ?? undefined,
|
||||
name: subscriber.name,
|
||||
commonName: subscriber.commonName,
|
||||
ttl: subscriber.ttl,
|
||||
ttl: subscriber.ttl ?? undefined,
|
||||
subjectAlternativeNames: subscriber.subjectAlternativeNames,
|
||||
keyUsages: subscriber.keyUsages as CertKeyUsage[],
|
||||
extendedKeyUsages: subscriber.extendedKeyUsages as CertExtendedKeyUsage[]
|
||||
@@ -219,7 +220,7 @@ export const registerPkiSubscriberRouter = async (server: FastifyZodProvider) =>
|
||||
caId: subscriber.caId ?? undefined,
|
||||
name: subscriber.name,
|
||||
commonName: subscriber.commonName,
|
||||
ttl: subscriber.ttl,
|
||||
ttl: subscriber.ttl ?? undefined,
|
||||
subjectAlternativeNames: subscriber.subjectAlternativeNames,
|
||||
keyUsages: subscriber.keyUsages as CertKeyUsage[],
|
||||
extendedKeyUsages: subscriber.extendedKeyUsages as CertExtendedKeyUsage[]
|
||||
|
||||
@@ -203,32 +203,44 @@ export const AcmeCertificateAuthorityFns = ({
|
||||
await appConnectionService.connectAppConnectionById(appConnection.app as AppConnection, dnsAppConnectionId, actor);
|
||||
|
||||
const caEntity = await certificateAuthorityDAL.transaction(async (tx) => {
|
||||
const ca = await certificateAuthorityDAL.create(
|
||||
{
|
||||
projectId,
|
||||
disableDirectIssuance
|
||||
},
|
||||
tx
|
||||
);
|
||||
|
||||
await externalCertificateAuthorityDAL.create(
|
||||
{
|
||||
certificateAuthorityId: ca.id,
|
||||
dnsAppConnectionId,
|
||||
type: CaType.ACME,
|
||||
name,
|
||||
configuration: {
|
||||
directoryUrl,
|
||||
accountEmail,
|
||||
dnsProvider: dnsProviderConfig.provider,
|
||||
hostedZoneId: dnsProviderConfig.hostedZoneId
|
||||
try {
|
||||
const ca = await certificateAuthorityDAL.create(
|
||||
{
|
||||
projectId,
|
||||
disableDirectIssuance
|
||||
},
|
||||
status
|
||||
},
|
||||
tx
|
||||
);
|
||||
tx
|
||||
);
|
||||
|
||||
return certificateAuthorityDAL.findByIdWithAssociatedCa(ca.id, tx);
|
||||
await externalCertificateAuthorityDAL.create(
|
||||
{
|
||||
certificateAuthorityId: ca.id,
|
||||
dnsAppConnectionId,
|
||||
type: CaType.ACME,
|
||||
name,
|
||||
projectId,
|
||||
configuration: {
|
||||
directoryUrl,
|
||||
accountEmail,
|
||||
dnsProvider: dnsProviderConfig.provider,
|
||||
hostedZoneId: dnsProviderConfig.hostedZoneId
|
||||
},
|
||||
status
|
||||
},
|
||||
tx
|
||||
);
|
||||
|
||||
return await certificateAuthorityDAL.findByIdWithAssociatedCa(ca.id, tx);
|
||||
} catch (error) {
|
||||
// @ts-expect-error We're expecting a database error
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||
if (error?.error?.code === "23505") {
|
||||
throw new BadRequestError({
|
||||
message: "Certificate authority with the same name already exists in your project"
|
||||
});
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
if (!caEntity.externalCa?.id) {
|
||||
|
||||
@@ -440,7 +440,7 @@ export const pkiSubscriberServiceFactory = ({
|
||||
|
||||
const caCertObj = new x509.X509Certificate(decryptedCaCert);
|
||||
const notBeforeDate = new Date();
|
||||
const notAfterDate = new Date(new Date().getTime() + ms(subscriber.ttl));
|
||||
const notAfterDate = new Date(new Date().getTime() + ms(subscriber.ttl ?? "0"));
|
||||
const caCertNotBeforeDate = new Date(caCertObj.notBefore);
|
||||
const caCertNotAfterDate = new Date(caCertObj.notAfter);
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ export type TCreatePkiSubscriberDTO = {
|
||||
name: string;
|
||||
commonName: string;
|
||||
status: PkiSubscriberStatus;
|
||||
ttl: string;
|
||||
ttl?: string;
|
||||
subjectAlternativeNames: string[];
|
||||
keyUsages: CertKeyUsage[];
|
||||
extendedKeyUsages: CertExtendedKeyUsage[];
|
||||
|
||||
@@ -24,7 +24,7 @@ export type TCreatePkiSubscriberDTO = {
|
||||
caId: string;
|
||||
name: string;
|
||||
commonName: string;
|
||||
ttl: string;
|
||||
ttl?: string;
|
||||
subjectAlternativeNames: string[];
|
||||
keyUsages: CertKeyUsage[];
|
||||
extendedKeyUsages: CertExtendedKeyUsage[];
|
||||
|
||||
@@ -45,7 +45,7 @@ const schema = z
|
||||
caId: z.string().min(1, "Issuing CA is required"),
|
||||
commonName: z.string().trim().min(1, "Common Name is required"),
|
||||
subjectAlternativeNames: z.string(),
|
||||
ttl: z.string().trim(),
|
||||
ttl: z.string().trim().optional(),
|
||||
keyUsages: z.object({
|
||||
[CertKeyUsage.DIGITAL_SIGNATURE]: z.boolean().optional(),
|
||||
[CertKeyUsage.KEY_ENCIPHERMENT]: z.boolean().optional(),
|
||||
@@ -90,6 +90,7 @@ export const PkiSubscriberModal = ({ popUp, handlePopUpToggle }: Props) => {
|
||||
handleSubmit,
|
||||
reset,
|
||||
setValue,
|
||||
watch,
|
||||
formState: { isSubmitting }
|
||||
} = useForm<FormData>({
|
||||
resolver: zodResolver(schema),
|
||||
@@ -107,6 +108,9 @@ export const PkiSubscriberModal = ({ popUp, handlePopUpToggle }: Props) => {
|
||||
}
|
||||
});
|
||||
|
||||
const selectedCaId = watch("caId");
|
||||
const selectedCa = cas?.find((ca) => ca.id === selectedCaId);
|
||||
|
||||
useEffect(() => {
|
||||
if (pkiSubscriber) {
|
||||
reset({
|
||||
@@ -314,97 +318,101 @@ export const PkiSubscriberModal = ({ popUp, handlePopUpToggle }: Props) => {
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
control={control}
|
||||
name="ttl"
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
label="TTL"
|
||||
isError={Boolean(error)}
|
||||
errorText={error?.message}
|
||||
isRequired
|
||||
>
|
||||
<Input {...field} placeholder="2 days, 1d, 2h, 1y, ..." />
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
<Accordion type="single" collapsible className="w-full">
|
||||
<AccordionItem value="key-usages" className="data-[state=open]:border-none">
|
||||
<AccordionTrigger className="h-fit flex-none pl-1 text-sm">
|
||||
<div className="order-1 ml-3">Key Usage</div>
|
||||
</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
<Controller
|
||||
control={control}
|
||||
name="keyUsages"
|
||||
render={({ field: { onChange, value }, fieldState: { error } }) => {
|
||||
return (
|
||||
<FormControl
|
||||
label="Key Usage"
|
||||
errorText={error?.message}
|
||||
isError={Boolean(error)}
|
||||
>
|
||||
<div className="mb-7 mt-2 grid grid-cols-2 gap-2">
|
||||
{KEY_USAGES_OPTIONS.map(({ label, value: optionValue }) => {
|
||||
return (
|
||||
<Checkbox
|
||||
id={optionValue}
|
||||
key={optionValue}
|
||||
className="data-[state=checked]:bg-primary"
|
||||
isChecked={value[optionValue]}
|
||||
onCheckedChange={(state) => {
|
||||
onChange({
|
||||
...value,
|
||||
[optionValue]: state
|
||||
});
|
||||
}}
|
||||
>
|
||||
{label}
|
||||
</Checkbox>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</FormControl>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<Controller
|
||||
control={control}
|
||||
name="extendedKeyUsages"
|
||||
render={({ field: { onChange, value }, fieldState: { error } }) => {
|
||||
return (
|
||||
<FormControl
|
||||
label="Extended Key Usage"
|
||||
errorText={error?.message}
|
||||
isError={Boolean(error)}
|
||||
>
|
||||
<div className="mb-7 mt-2 grid grid-cols-2 gap-2">
|
||||
{EXTENDED_KEY_USAGES_OPTIONS.map(({ label, value: optionValue }) => {
|
||||
return (
|
||||
<Checkbox
|
||||
id={optionValue}
|
||||
key={optionValue}
|
||||
className="data-[state=checked]:bg-primary"
|
||||
isChecked={value[optionValue]}
|
||||
onCheckedChange={(state) => {
|
||||
onChange({
|
||||
...value,
|
||||
[optionValue]: state
|
||||
});
|
||||
}}
|
||||
>
|
||||
{label}
|
||||
</Checkbox>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</FormControl>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
{selectedCa?.type !== CaType.ACME && (
|
||||
<Controller
|
||||
control={control}
|
||||
name="ttl"
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
label="TTL"
|
||||
isError={Boolean(error)}
|
||||
errorText={error?.message}
|
||||
isRequired
|
||||
>
|
||||
<Input {...field} placeholder="2 days, 1d, 2h, 1y, ..." />
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
{selectedCa?.type !== CaType.ACME && (
|
||||
<Accordion type="single" collapsible className="w-full">
|
||||
<AccordionItem value="key-usages" className="data-[state=open]:border-none">
|
||||
<AccordionTrigger className="h-fit flex-none pl-1 text-sm">
|
||||
<div className="order-1 ml-3">Key Usage</div>
|
||||
</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
<Controller
|
||||
control={control}
|
||||
name="keyUsages"
|
||||
render={({ field: { onChange, value }, fieldState: { error } }) => {
|
||||
return (
|
||||
<FormControl
|
||||
label="Key Usage"
|
||||
errorText={error?.message}
|
||||
isError={Boolean(error)}
|
||||
>
|
||||
<div className="mb-7 mt-2 grid grid-cols-2 gap-2">
|
||||
{KEY_USAGES_OPTIONS.map(({ label, value: optionValue }) => {
|
||||
return (
|
||||
<Checkbox
|
||||
id={optionValue}
|
||||
key={optionValue}
|
||||
className="data-[state=checked]:bg-primary"
|
||||
isChecked={value[optionValue]}
|
||||
onCheckedChange={(state) => {
|
||||
onChange({
|
||||
...value,
|
||||
[optionValue]: state
|
||||
});
|
||||
}}
|
||||
>
|
||||
{label}
|
||||
</Checkbox>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</FormControl>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<Controller
|
||||
control={control}
|
||||
name="extendedKeyUsages"
|
||||
render={({ field: { onChange, value }, fieldState: { error } }) => {
|
||||
return (
|
||||
<FormControl
|
||||
label="Extended Key Usage"
|
||||
errorText={error?.message}
|
||||
isError={Boolean(error)}
|
||||
>
|
||||
<div className="mb-7 mt-2 grid grid-cols-2 gap-2">
|
||||
{EXTENDED_KEY_USAGES_OPTIONS.map(({ label, value: optionValue }) => {
|
||||
return (
|
||||
<Checkbox
|
||||
id={optionValue}
|
||||
key={optionValue}
|
||||
className="data-[state=checked]:bg-primary"
|
||||
isChecked={value[optionValue]}
|
||||
onCheckedChange={(state) => {
|
||||
onChange({
|
||||
...value,
|
||||
[optionValue]: state
|
||||
});
|
||||
}}
|
||||
>
|
||||
{label}
|
||||
</Checkbox>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</FormControl>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
)}
|
||||
<div className="mt-4 flex items-center">
|
||||
<Button
|
||||
className="mr-4"
|
||||
|
||||
Reference in New Issue
Block a user