Merge pull request #1631 from akhilmhdh/fix/dyn-superuser-remove

fix(server): resolved failing to use dynamic secret due to superuser
This commit is contained in:
Maidul Islam
2024-03-27 11:19:51 -04:00
committed by GitHub
23 changed files with 300 additions and 272 deletions
+2 -2
View File
@@ -3,6 +3,8 @@ import "fastify";
import { TUsers } from "@app/db/schemas"; import { TUsers } from "@app/db/schemas";
import { TAuditLogServiceFactory } from "@app/ee/services/audit-log/audit-log-service"; import { TAuditLogServiceFactory } from "@app/ee/services/audit-log/audit-log-service";
import { TCreateAuditLogDTO } from "@app/ee/services/audit-log/audit-log-types"; import { TCreateAuditLogDTO } from "@app/ee/services/audit-log/audit-log-types";
import { TDynamicSecretServiceFactory } from "@app/ee/services/dynamic-secret/dynamic-secret-service";
import { TDynamicSecretLeaseServiceFactory } from "@app/ee/services/dynamic-secret-lease/dynamic-secret-lease-service";
import { TLdapConfigServiceFactory } from "@app/ee/services/ldap-config/ldap-config-service"; import { TLdapConfigServiceFactory } from "@app/ee/services/ldap-config/ldap-config-service";
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service"; import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
@@ -21,8 +23,6 @@ import { TAuthPasswordFactory } from "@app/services/auth/auth-password-service";
import { TAuthSignupFactory } from "@app/services/auth/auth-signup-service"; import { TAuthSignupFactory } from "@app/services/auth/auth-signup-service";
import { ActorAuthMethod, ActorType } from "@app/services/auth/auth-type"; import { ActorAuthMethod, ActorType } from "@app/services/auth/auth-type";
import { TAuthTokenServiceFactory } from "@app/services/auth-token/auth-token-service"; import { TAuthTokenServiceFactory } from "@app/services/auth-token/auth-token-service";
import { TDynamicSecretServiceFactory } from "@app/services/dynamic-secret/dynamic-secret-service";
import { TDynamicSecretLeaseServiceFactory } from "@app/services/dynamic-secret-lease/dynamic-secret-lease-service";
import { TIdentityServiceFactory } from "@app/services/identity/identity-service"; import { TIdentityServiceFactory } from "@app/services/identity/identity-service";
import { TIdentityAccessTokenServiceFactory } from "@app/services/identity-access-token/identity-access-token-service"; import { TIdentityAccessTokenServiceFactory } from "@app/services/identity-access-token/identity-access-token-service";
import { TIdentityProjectServiceFactory } from "@app/services/identity-project/identity-project-service"; import { TIdentityProjectServiceFactory } from "@app/services/identity-project/identity-project-service";
@@ -6,10 +6,9 @@ import { DYNAMIC_SECRET_LEASES } from "@app/lib/api-docs";
import { daysToMillisecond } from "@app/lib/dates"; import { daysToMillisecond } from "@app/lib/dates";
import { removeTrailingSlash } from "@app/lib/fn"; import { removeTrailingSlash } from "@app/lib/fn";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { SanitizedDynamicSecretSchema } from "@app/server/routes/sanitizedSchemas";
import { AuthMode } from "@app/services/auth/auth-type"; import { AuthMode } from "@app/services/auth/auth-type";
import { SanitizedDynamicSecretSchema } from "../sanitizedSchemas";
export const registerDynamicSecretLeaseRouter = async (server: FastifyZodProvider) => { export const registerDynamicSecretLeaseRouter = async (server: FastifyZodProvider) => {
server.route({ server.route({
url: "/", url: "/",
@@ -3,14 +3,13 @@ import ms from "ms";
import { z } from "zod"; import { z } from "zod";
import { DynamicSecretLeasesSchema } from "@app/db/schemas"; import { DynamicSecretLeasesSchema } from "@app/db/schemas";
import { DynamicSecretProviderSchema } from "@app/ee/services/dynamic-secret/providers/models";
import { DYNAMIC_SECRETS } from "@app/lib/api-docs"; import { DYNAMIC_SECRETS } from "@app/lib/api-docs";
import { daysToMillisecond } from "@app/lib/dates"; import { daysToMillisecond } from "@app/lib/dates";
import { removeTrailingSlash } from "@app/lib/fn"; import { removeTrailingSlash } from "@app/lib/fn";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { SanitizedDynamicSecretSchema } from "@app/server/routes/sanitizedSchemas";
import { AuthMode } from "@app/services/auth/auth-type"; import { AuthMode } from "@app/services/auth/auth-type";
import { DynamicSecretProviderSchema } from "@app/services/dynamic-secret/providers/models";
import { SanitizedDynamicSecretSchema } from "../sanitizedSchemas";
export const registerDynamicSecretRouter = async (server: FastifyZodProvider) => { export const registerDynamicSecretRouter = async (server: FastifyZodProvider) => {
server.route({ server.route({
+11
View File
@@ -1,3 +1,5 @@
import { registerDynamicSecretLeaseRouter } from "./dynamic-secret-lease-router";
import { registerDynamicSecretRouter } from "./dynamic-secret-router";
import { registerLdapRouter } from "./ldap-router"; import { registerLdapRouter } from "./ldap-router";
import { registerLicenseRouter } from "./license-router"; import { registerLicenseRouter } from "./license-router";
import { registerOrgRoleRouter } from "./org-role-router"; import { registerOrgRoleRouter } from "./org-role-router";
@@ -34,6 +36,15 @@ export const registerV1EERoutes = async (server: FastifyZodProvider) => {
await server.register(registerSecretRotationProviderRouter, { await server.register(registerSecretRotationProviderRouter, {
prefix: "/secret-rotation-providers" prefix: "/secret-rotation-providers"
}); });
await server.register(
async (dynamicSecretRouter) => {
await dynamicSecretRouter.register(registerDynamicSecretRouter);
await dynamicSecretRouter.register(registerDynamicSecretLeaseRouter, { prefix: "/leases" });
},
{ prefix: "/dynamic-secrets" }
);
await server.register(registerSamlRouter, { prefix: "/sso" }); await server.register(registerSamlRouter, { prefix: "/sso" });
await server.register(registerScimRouter, { prefix: "/scim" }); await server.register(registerScimRouter, { prefix: "/scim" });
await server.register(registerLdapRouter, { prefix: "/ldap" }); await server.register(registerLdapRouter, { prefix: "/ldap" });
@@ -8,11 +8,12 @@ import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services
import { getConfig } from "@app/lib/config/env"; import { getConfig } from "@app/lib/config/env";
import { infisicalSymmetricDecrypt } from "@app/lib/crypto/encryption"; import { infisicalSymmetricDecrypt } from "@app/lib/crypto/encryption";
import { BadRequestError } from "@app/lib/errors"; import { BadRequestError } from "@app/lib/errors";
import { logger } from "@app/lib/logger";
import { TProjectDALFactory } from "@app/services/project/project-dal";
import { TSecretFolderDALFactory } from "@app/services/secret-folder/secret-folder-dal";
import { TDynamicSecretDALFactory } from "../dynamic-secret/dynamic-secret-dal"; import { TDynamicSecretDALFactory } from "../dynamic-secret/dynamic-secret-dal";
import { DynamicSecretProviders, TDynamicProviderFns } from "../dynamic-secret/providers/models"; import { DynamicSecretProviders, TDynamicProviderFns } from "../dynamic-secret/providers/models";
import { TProjectDALFactory } from "../project/project-dal";
import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal";
import { TDynamicSecretLeaseDALFactory } from "./dynamic-secret-lease-dal"; import { TDynamicSecretLeaseDALFactory } from "./dynamic-secret-lease-dal";
import { TDynamicSecretLeaseQueueServiceFactory } from "./dynamic-secret-lease-queue"; import { TDynamicSecretLeaseQueueServiceFactory } from "./dynamic-secret-lease-queue";
import { import {
@@ -248,6 +249,7 @@ export const dynamicSecretLeaseServiceFactory = ({
if ((revokeResponse as { error?: Error })?.error) { if ((revokeResponse as { error?: Error })?.error) {
const { error } = revokeResponse as { error?: Error }; const { error } = revokeResponse as { error?: Error };
logger.error("Failed to revoke lease", { error: error?.message });
const deletedDynamicSecretLease = await dynamicSecretLeaseDAL.updateById(dynamicSecretLease.id, { const deletedDynamicSecretLease = await dynamicSecretLeaseDAL.updateById(dynamicSecretLease.id, {
status: DynamicSecretLeaseStatus.FailedDeletion, status: DynamicSecretLeaseStatus.FailedDeletion,
statusDetails: error?.message?.slice(0, 255) statusDetails: error?.message?.slice(0, 255)
@@ -6,11 +6,11 @@ import { TPermissionServiceFactory } from "@app/ee/services/permission/permissio
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
import { infisicalSymmetricDecrypt, infisicalSymmetricEncypt } from "@app/lib/crypto/encryption"; import { infisicalSymmetricDecrypt, infisicalSymmetricEncypt } from "@app/lib/crypto/encryption";
import { BadRequestError } from "@app/lib/errors"; import { BadRequestError } from "@app/lib/errors";
import { TProjectDALFactory } from "@app/services/project/project-dal";
import { TSecretFolderDALFactory } from "@app/services/secret-folder/secret-folder-dal";
import { TDynamicSecretLeaseDALFactory } from "../dynamic-secret-lease/dynamic-secret-lease-dal"; import { TDynamicSecretLeaseDALFactory } from "../dynamic-secret-lease/dynamic-secret-lease-dal";
import { TDynamicSecretLeaseQueueServiceFactory } from "../dynamic-secret-lease/dynamic-secret-lease-queue"; import { TDynamicSecretLeaseQueueServiceFactory } from "../dynamic-secret-lease/dynamic-secret-lease-queue";
import { TProjectDALFactory } from "../project/project-dal";
import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal";
import { TDynamicSecretDALFactory } from "./dynamic-secret-dal"; import { TDynamicSecretDALFactory } from "./dynamic-secret-dal";
import { import {
DynamicSecretStatus, DynamicSecretStatus,
@@ -23,7 +23,17 @@ export const SqlDatabaseProvider = (): TDynamicProviderFns => {
const dbHost = appCfg.DB_HOST || getDbConnectionHost(appCfg.DB_CONNECTION_URI); const dbHost = appCfg.DB_HOST || getDbConnectionHost(appCfg.DB_CONNECTION_URI);
const providerInputs = await DynamicSecretSqlDBSchema.parseAsync(inputs); const providerInputs = await DynamicSecretSqlDBSchema.parseAsync(inputs);
if (providerInputs.host === "localhost" || providerInputs.host === "127.0.0.1" || dbHost === providerInputs.host) if (
// localhost
providerInputs.host === "localhost" ||
providerInputs.host === "127.0.0.1" ||
// database infisical uses
dbHost === providerInputs.host ||
// internal ips
providerInputs.host === "host.docker.internal" ||
providerInputs.host.match(/^10\.\d+\.\d+\.\d+/) ||
providerInputs.host.match(/^192\.168\.\d+\.\d+/)
)
throw new BadRequestError({ message: "Invalid db host" }); throw new BadRequestError({ message: "Invalid db host" });
return providerInputs; return providerInputs;
}; };
@@ -90,7 +90,17 @@ export const secretRotationDbFn = async ({
const appCfg = getConfig(); const appCfg = getConfig();
const ssl = ca ? { rejectUnauthorized: false, ca } : undefined; const ssl = ca ? { rejectUnauthorized: false, ca } : undefined;
if (host === "localhost" || host === "127.0.0.1" || getDbConnectionHost(appCfg.DB_CONNECTION_URI) === host) const dbHost = appCfg.DB_HOST || getDbConnectionHost(appCfg.DB_CONNECTION_URI);
if (
host === "localhost" ||
host === "127.0.0.1" ||
// database infisical uses
dbHost === host ||
// internal ips
host === "host.docker.internal" ||
host.match(/^10\.\d+\.\d+\.\d+/) ||
host.match(/^192\.168\.\d+\.\d+/)
)
throw new Error("Invalid db host"); throw new Error("Invalid db host");
const db = knex({ const db = knex({
+6 -6
View File
@@ -5,6 +5,12 @@ import { registerV1EERoutes } from "@app/ee/routes/v1";
import { auditLogDALFactory } from "@app/ee/services/audit-log/audit-log-dal"; import { auditLogDALFactory } from "@app/ee/services/audit-log/audit-log-dal";
import { auditLogQueueServiceFactory } from "@app/ee/services/audit-log/audit-log-queue"; import { auditLogQueueServiceFactory } from "@app/ee/services/audit-log/audit-log-queue";
import { auditLogServiceFactory } from "@app/ee/services/audit-log/audit-log-service"; import { auditLogServiceFactory } from "@app/ee/services/audit-log/audit-log-service";
import { dynamicSecretDALFactory } from "@app/ee/services/dynamic-secret/dynamic-secret-dal";
import { dynamicSecretServiceFactory } from "@app/ee/services/dynamic-secret/dynamic-secret-service";
import { buildDynamicSecretProviders } from "@app/ee/services/dynamic-secret/providers";
import { dynamicSecretLeaseDALFactory } from "@app/ee/services/dynamic-secret-lease/dynamic-secret-lease-dal";
import { dynamicSecretLeaseQueueServiceFactory } from "@app/ee/services/dynamic-secret-lease/dynamic-secret-lease-queue";
import { dynamicSecretLeaseServiceFactory } from "@app/ee/services/dynamic-secret-lease/dynamic-secret-lease-service";
import { ldapConfigDALFactory } from "@app/ee/services/ldap-config/ldap-config-dal"; import { ldapConfigDALFactory } from "@app/ee/services/ldap-config/ldap-config-dal";
import { ldapConfigServiceFactory } from "@app/ee/services/ldap-config/ldap-config-service"; import { ldapConfigServiceFactory } from "@app/ee/services/ldap-config/ldap-config-service";
import { licenseDALFactory } from "@app/ee/services/license/license-dal"; import { licenseDALFactory } from "@app/ee/services/license/license-dal";
@@ -47,12 +53,6 @@ import { authPaswordServiceFactory } from "@app/services/auth/auth-password-serv
import { authSignupServiceFactory } from "@app/services/auth/auth-signup-service"; import { authSignupServiceFactory } from "@app/services/auth/auth-signup-service";
import { tokenDALFactory } from "@app/services/auth-token/auth-token-dal"; import { tokenDALFactory } from "@app/services/auth-token/auth-token-dal";
import { tokenServiceFactory } from "@app/services/auth-token/auth-token-service"; import { tokenServiceFactory } from "@app/services/auth-token/auth-token-service";
import { dynamicSecretDALFactory } from "@app/services/dynamic-secret/dynamic-secret-dal";
import { dynamicSecretServiceFactory } from "@app/services/dynamic-secret/dynamic-secret-service";
import { buildDynamicSecretProviders } from "@app/services/dynamic-secret/providers";
import { dynamicSecretLeaseDALFactory } from "@app/services/dynamic-secret-lease/dynamic-secret-lease-dal";
import { dynamicSecretLeaseQueueServiceFactory } from "@app/services/dynamic-secret-lease/dynamic-secret-lease-queue";
import { dynamicSecretLeaseServiceFactory } from "@app/services/dynamic-secret-lease/dynamic-secret-lease-service";
import { identityDALFactory } from "@app/services/identity/identity-dal"; import { identityDALFactory } from "@app/services/identity/identity-dal";
import { identityOrgDALFactory } from "@app/services/identity/identity-org-dal"; import { identityOrgDALFactory } from "@app/services/identity/identity-org-dal";
import { identityServiceFactory } from "@app/services/identity/identity-service"; import { identityServiceFactory } from "@app/services/identity/identity-service";
-10
View File
@@ -1,8 +1,6 @@
import { registerAdminRouter } from "./admin-router"; import { registerAdminRouter } from "./admin-router";
import { registerAuthRoutes } from "./auth-router"; import { registerAuthRoutes } from "./auth-router";
import { registerProjectBotRouter } from "./bot-router"; import { registerProjectBotRouter } from "./bot-router";
import { registerDynamicSecretLeaseRouter } from "./dynamic-secret-lease-router";
import { registerDynamicSecretRouter } from "./dynamic-secret-router";
import { registerIdentityAccessTokenRouter } from "./identity-access-token-router"; import { registerIdentityAccessTokenRouter } from "./identity-access-token-router";
import { registerIdentityRouter } from "./identity-router"; import { registerIdentityRouter } from "./identity-router";
import { registerIdentityUaRouter } from "./identity-ua"; import { registerIdentityUaRouter } from "./identity-ua";
@@ -54,14 +52,6 @@ export const registerV1Routes = async (server: FastifyZodProvider) => {
{ prefix: "/workspace" } { prefix: "/workspace" }
); );
await server.register(
async (dynamicSecretRouter) => {
await dynamicSecretRouter.register(registerDynamicSecretRouter);
await dynamicSecretRouter.register(registerDynamicSecretLeaseRouter, { prefix: "/leases" });
},
{ prefix: "/dynamic-secrets" }
);
await server.register(registerProjectBotRouter, { prefix: "/bot" }); await server.register(registerProjectBotRouter, { prefix: "/bot" });
await server.register(registerIntegrationRouter, { prefix: "/integration" }); await server.register(registerIntegrationRouter, { prefix: "/integration" });
await server.register(registerIntegrationAuthRouter, { prefix: "/integration-auth" }); await server.register(registerIntegrationAuthRouter, { prefix: "/integration-auth" });
+12 -4
View File
@@ -1,4 +1,4 @@
import { forwardRef, ReactNode } from "react"; import { CSSProperties, forwardRef, ReactNode } from "react";
import { twMerge } from "tailwind-merge"; import { twMerge } from "tailwind-merge";
export type CardTitleProps = { export type CardTitleProps = {
@@ -31,10 +31,13 @@ export const CardFooter = ({ children, className }: CardFooterProps) => (
export type CardBodyProps = { export type CardBodyProps = {
children: ReactNode; children: ReactNode;
className?: string; className?: string;
style?: CSSProperties;
}; };
export const CardBody = ({ children, className }: CardBodyProps) => ( export const CardBody = ({ children, className, style }: CardBodyProps) => (
<div className={twMerge("px-6 pb-6 pt-0", className)}>{children}</div> <div className={twMerge("px-6 pb-6 pt-0", className)} style={style}>
{children}
</div>
); );
export type CardProps = { export type CardProps = {
@@ -44,10 +47,14 @@ export type CardProps = {
isRounded?: boolean; isRounded?: boolean;
isPlain?: boolean; isPlain?: boolean;
isHoverable?: boolean; isHoverable?: boolean;
style?: CSSProperties;
}; };
export const Card = forwardRef<HTMLDivElement, CardProps>( export const Card = forwardRef<HTMLDivElement, CardProps>(
({ children, isFullHeight, isRounded, isHoverable, isPlain, className }, ref): JSX.Element => { (
{ children, isFullHeight, isRounded, isHoverable, isPlain, className, style },
ref
): JSX.Element => {
return ( return (
<div <div
ref={ref} ref={ref}
@@ -59,6 +66,7 @@ export const Card = forwardRef<HTMLDivElement, CardProps>(
isHoverable && "hover:shadow-xl", isHoverable && "hover:shadow-xl",
className className
)} )}
style={style}
> >
{children} {children}
</div> </div>
+5 -2
View File
@@ -29,12 +29,15 @@ export const ModalContent = forwardRef<HTMLDivElement, ModalContentProps>(
<Card <Card
isRounded isRounded
className={twMerge( className={twMerge(
"thin-scrollbar fixed top-1/2 left-1/2 z-30 max-h-screen max-w-xl -translate-y-2/4 -translate-x-2/4 animate-popIn border border-mineshaft-600 drop-shadow-2xl dark:[color-scheme:dark]", "thin-scrollbar fixed top-1/2 left-1/2 z-30 max-w-xl -translate-y-2/4 -translate-x-2/4 animate-popIn border border-mineshaft-600 drop-shadow-2xl dark:[color-scheme:dark]",
className className
)} )}
style={{ maxHeight: "90%" }}
> >
{title && <CardTitle subTitle={subTitle}>{title}</CardTitle>} {title && <CardTitle subTitle={subTitle}>{title}</CardTitle>}
<CardBody>{children}</CardBody> <CardBody className="overflow-y-auto overflow-x-hidden" style={{ maxHeight: "90%" }}>
{children}
</CardBody>
{footerContent && <CardFooter>{footerContent}</CardFooter>} {footerContent && <CardFooter>{footerContent}</CardFooter>}
<DialogPrimitive.Close aria-label="Close" asChild onClick={onClose}> <DialogPrimitive.Close aria-label="Close" asChild onClick={onClose}>
<IconButton <IconButton
@@ -11,7 +11,7 @@ import { SqlDatabaseInputForm } from "./SqlDatabaseInputForm";
type Props = { type Props = {
isOpen?: boolean; isOpen?: boolean;
onToggle: (isOpen: boolean) => void; onToggle: (isOpen: boolean) => void;
projectSlug:string; projectSlug: string;
environment: string; environment: string;
secretPath: string; secretPath: string;
}; };
@@ -42,7 +42,7 @@ export const CreateDynamicSecretForm = ({
<ModalContent <ModalContent
title="Dynamic secret setup" title="Dynamic secret setup"
subTitle="Configure dynamic secret parameters" subTitle="Configure dynamic secret parameters"
className="my-4 max-h-screen max-w-3xl overflow-scroll" className="my-4 max-w-3xl"
> >
<AnimatePresence exitBeforeEnter> <AnimatePresence exitBeforeEnter>
{wizardStep === WizardSteps.SelectProvider && ( {wizardStep === WizardSteps.SelectProvider && (
@@ -56,7 +56,7 @@ export const CreateDynamicSecretForm = ({
<div className="mb-4 text-mineshaft-300">Select a service to connect to:</div> <div className="mb-4 text-mineshaft-300">Select a service to connect to:</div>
<div className="flex items-center space-x-4"> <div className="flex items-center space-x-4">
<div <div
className="flex h-32 w-32 cursor-pointer flex-col items-center space-y-4 rounded border border-mineshaft-500 bg-bunker-600 p-6 transition-all hover:bg-primary/10 hover:border-primary/70 hover:text-white" className="flex h-32 w-32 cursor-pointer flex-col items-center space-y-4 rounded border border-mineshaft-500 bg-bunker-600 p-6 transition-all hover:border-primary/70 hover:bg-primary/10 hover:text-white"
role="button" role="button"
tabIndex={0} tabIndex={0}
onClick={() => { onClick={() => {
@@ -25,7 +25,7 @@ const formSchema = z.object({
provider: z.object({ provider: z.object({
client: z.nativeEnum(SqlProviders), client: z.nativeEnum(SqlProviders),
host: z.string().toLowerCase().min(1), host: z.string().toLowerCase().min(1),
port: z.number(), port: z.coerce.number(),
database: z.string().min(1), database: z.string().min(1),
username: z.string().min(1), username: z.string().min(1),
password: z.string().min(1), password: z.string().min(1),
@@ -54,9 +54,7 @@ const formSchema = z.object({
if (valMs > 24 * 60 * 60 * 1000) if (valMs > 24 * 60 * 60 * 1000)
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" }); ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" });
}), }),
name: z name: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase")
.string()
.refine((val) => val.toLowerCase() === val, "Must be lowercase")
}); });
type TForm = z.infer<typeof formSchema>; type TForm = z.infer<typeof formSchema>;
@@ -84,14 +82,14 @@ export const SqlDatabaseInputForm = ({
defaultValues: { defaultValues: {
provider: { provider: {
creationStatement: creationStatement:
"CREATE USER \"{{username}}\" WITH SUPERUSER ENCRYPTED PASSWORD '{{password}}' VALID UNTIL '{{expiration}}';\nGRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO \"{{username}}\";", "CREATE USER \"{{username}}\" WITH ENCRYPTED PASSWORD '{{password}}' VALID UNTIL '{{expiration}}';\nGRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO \"{{username}}\";",
renewStatement: "ALTER ROLE \"{{username}}\" VALID UNTIL '{{expiration}}';", renewStatement: "ALTER ROLE \"{{username}}\" VALID UNTIL '{{expiration}}';",
revocationStatement: revocationStatement:
'REVOKE ALL PRIVILEGES ON ALL TABLES IN SCHEMA public FROM "{{username}}";\nDROP OWNED BY "{{username}}";\nDROP ROLE "{{username}}";' 'REVOKE ALL PRIVILEGES ON ALL TABLES IN SCHEMA public FROM "{{username}}";\nDROP ROLE "{{username}}";'
} }
} }
}); });
const createDynamicSecret = useCreateDynamicSecret(); const createDynamicSecret = useCreateDynamicSecret();
const handleCreateDynamicSecret = async ({ name, maxTTL, provider, defaultTTL }: TForm) => { const handleCreateDynamicSecret = async ({ name, maxTTL, provider, defaultTTL }: TForm) => {
@@ -119,238 +117,236 @@ export const SqlDatabaseInputForm = ({
return ( return (
<div> <div>
<form onSubmit={handleSubmit(handleCreateDynamicSecret)}> <form onSubmit={handleSubmit(handleCreateDynamicSecret)}>
<div className="flex items-center space-x-2">
<div className="flex-grow">
<Controller
control={control}
defaultValue=""
name="name"
render={({ field, fieldState: { error } }) => (
<FormControl
label="Secret Name"
isError={Boolean(error)}
errorText={error?.message}
>
<Input {...field} placeholder="dynamic-postgres" />
</FormControl>
)}
/>
</div>
<div className="w-32">
<Controller
control={control}
name="defaultTTL"
defaultValue="1h"
render={({ field, fieldState: { error } }) => (
<FormControl
label={<TtlFormLabel label="Default TTL" />}
isError={Boolean(error?.message)}
errorText={error?.message}
>
<Input {...field} />
</FormControl>
)}
/>
</div>
<div className="w-32">
<Controller
control={control}
name="maxTTL"
defaultValue="24h"
render={({ field, fieldState: { error } }) => (
<FormControl
label={<TtlFormLabel label="Max TTL" />}
isError={Boolean(error?.message)}
errorText={error?.message}
>
<Input {...field} />
</FormControl>
)}
/>
</div>
</div>
<div> <div>
<div className="mb-4 mt-4 border-b border-mineshaft-500 pb-2 pl-1 font-medium text-mineshaft-200"> <div className="flex items-center space-x-2">
Configuration <div className="flex-grow">
<Controller
control={control}
defaultValue=""
name="name"
render={({ field, fieldState: { error } }) => (
<FormControl
label="Secret Name"
isError={Boolean(error)}
errorText={error?.message}
>
<Input {...field} placeholder="dynamic-postgres" />
</FormControl>
)}
/>
</div>
<div className="w-32">
<Controller
control={control}
name="defaultTTL"
defaultValue="1h"
render={({ field, fieldState: { error } }) => (
<FormControl
label={<TtlFormLabel label="Default TTL" />}
isError={Boolean(error?.message)}
errorText={error?.message}
>
<Input {...field} />
</FormControl>
)}
/>
</div>
<div className="w-32">
<Controller
control={control}
name="maxTTL"
defaultValue="24h"
render={({ field, fieldState: { error } }) => (
<FormControl
label={<TtlFormLabel label="Max TTL" />}
isError={Boolean(error?.message)}
errorText={error?.message}
>
<Input {...field} />
</FormControl>
)}
/>
</div>
</div> </div>
<div className="flex flex-col"> <div>
<div className="pl-1 pb-0.5 text-sm text-mineshaft-400">Service</div> <div className="mb-4 mt-4 border-b border-mineshaft-500 pb-2 pl-1 font-medium text-mineshaft-200">
<Controller Configuration
control={control}
name="provider.client"
defaultValue={SqlProviders.Postgres}
render={({ field: { value, onChange }, fieldState: { error } }) => (
<FormControl isError={Boolean(error?.message)} errorText={error?.message}>
<Select
value={value}
onValueChange={(val) => onChange(val)}
className="w-full border border-mineshaft-500"
>
<SelectItem value={SqlProviders.Postgres}>PostgreSQL</SelectItem>
</Select>
</FormControl>
)}
/>
<div className="flex items-center space-x-2">
<Controller
control={control}
name="provider.host"
defaultValue=""
render={({ field, fieldState: { error } }) => (
<FormControl
label="Host"
className="flex-grow"
isError={Boolean(error?.message)}
errorText={error?.message}
>
<Input {...field} />
</FormControl>
)}
/>
<Controller
control={control}
name="provider.port"
defaultValue={5432}
render={({ field, fieldState: { error } }) => (
<FormControl
label="Port"
isError={Boolean(error?.message)}
errorText={error?.message}
>
<Input
{...field}
type="number"
onChange={(el) => field.onChange(parseInt(el.target.value, 10))}
/>
</FormControl>
)}
/>
</div> </div>
<div className="flex items-center space-x-2"> <div className="flex flex-col">
<div className="pl-1 pb-0.5 text-sm text-mineshaft-400">Service</div>
<Controller <Controller
control={control} control={control}
name="provider.username" name="provider.client"
defaultValue="" defaultValue={SqlProviders.Postgres}
render={({ field, fieldState: { error } }) => ( render={({ field: { value, onChange }, fieldState: { error } }) => (
<FormControl <FormControl isError={Boolean(error?.message)} errorText={error?.message}>
label="User" <Select
isError={Boolean(error?.message)} value={value}
errorText={error?.message} onValueChange={(val) => onChange(val)}
> className="w-full border border-mineshaft-500"
<Input {...field} /> >
<SelectItem value={SqlProviders.Postgres}>PostgreSQL</SelectItem>
</Select>
</FormControl> </FormControl>
)} )}
/> />
<Controller <div className="flex items-center space-x-2">
control={control} <Controller
name="provider.password" control={control}
render={({ field, fieldState: { error } }) => ( name="provider.host"
<FormControl defaultValue=""
label="Password" render={({ field, fieldState: { error } }) => (
isError={Boolean(error?.message)} <FormControl
errorText={error?.message} label="Host"
> className="flex-grow"
<Input {...field} type="password" /> isError={Boolean(error?.message)}
</FormControl> errorText={error?.message}
)} >
/> <Input {...field} />
<Controller </FormControl>
control={control} )}
name="provider.database" />
defaultValue="default" <Controller
render={({ field, fieldState: { error } }) => ( control={control}
<FormControl name="provider.port"
label="Database Name" defaultValue={5432}
isError={Boolean(error?.message)} render={({ field, fieldState: { error } }) => (
errorText={error?.message} <FormControl
> label="Port"
<Input {...field} /> isError={Boolean(error?.message)}
</FormControl> errorText={error?.message}
)} >
/> <Input {...field} type="number" />
</div> </FormControl>
<div> )}
<Controller />
control={control} </div>
name="provider.ca" <div className="flex items-center space-x-2">
render={({ field, fieldState: { error } }) => ( <Controller
<FormControl control={control}
isOptional name="provider.username"
label="CA(SSL)" defaultValue=""
isError={Boolean(error?.message)} render={({ field, fieldState: { error } }) => (
errorText={error?.message} <FormControl
> label="User"
<SecretInput isError={Boolean(error?.message)}
{...field} errorText={error?.message}
containerClassName="text-bunker-300 hover:border-primary-400/50 border border-mineshaft-600 bg-mineshaft-900 px-2 py-1.5" >
/> <Input {...field} />
</FormControl> </FormControl>
)} )}
/> />
<Accordion type="single" collapsible className="mb-2 w-full bg-mineshaft-700"> <Controller
<AccordionItem value="advance-statements"> control={control}
<AccordionTrigger>Modify SQL Statements</AccordionTrigger> name="provider.password"
<AccordionContent> render={({ field, fieldState: { error } }) => (
<Controller <FormControl
control={control} label="Password"
name="provider.creationStatement" isError={Boolean(error?.message)}
render={({ field, fieldState: { error } }) => ( errorText={error?.message}
<FormControl >
label="Creation Statement" <Input {...field} type="password" />
isError={Boolean(error?.message)} </FormControl>
errorText={error?.message} )}
helperText="username, password and expiration are dynamically provisioned" />
> <Controller
<TextArea control={control}
{...field} name="provider.database"
reSize="none" defaultValue="default"
rows={3} render={({ field, fieldState: { error } }) => (
className="border-mineshaft-600 bg-mineshaft-900 text-sm" <FormControl
/> label="Database Name"
</FormControl> isError={Boolean(error?.message)}
)} errorText={error?.message}
/> >
<Controller <Input {...field} />
control={control} </FormControl>
name="provider.revocationStatement" )}
render={({ field, fieldState: { error } }) => ( />
<FormControl </div>
label="Revocation Statement" <div>
isError={Boolean(error?.message)} <Controller
errorText={error?.message} control={control}
helperText="username is dynamically provisioned" name="provider.ca"
> render={({ field, fieldState: { error } }) => (
<TextArea <FormControl
{...field} isOptional
reSize="none" label="CA(SSL)"
rows={3} isError={Boolean(error?.message)}
className="border-mineshaft-600 bg-mineshaft-900 text-sm" errorText={error?.message}
/> >
</FormControl> <SecretInput
)} {...field}
/> containerClassName="text-bunker-300 hover:border-primary-400/50 border border-mineshaft-600 bg-mineshaft-900 px-2 py-1.5"
<Controller />
control={control} </FormControl>
name="provider.renewStatement" )}
render={({ field, fieldState: { error } }) => ( />
<FormControl <Accordion type="single" collapsible className="mb-2 w-full bg-mineshaft-700">
label="Renew Statement" <AccordionItem value="advance-statements">
helperText="username and expiration are dynamically provisioned" <AccordionTrigger>Modify SQL Statements</AccordionTrigger>
isError={Boolean(error?.message)} <AccordionContent>
errorText={error?.message} <Controller
> control={control}
<TextArea name="provider.creationStatement"
{...field} render={({ field, fieldState: { error } }) => (
reSize="none" <FormControl
rows={3} label="Creation Statement"
className="border-mineshaft-600 bg-mineshaft-900 text-sm" isError={Boolean(error?.message)}
/> errorText={error?.message}
</FormControl> helperText="username, password and expiration are dynamically provisioned"
)} >
/> <TextArea
</AccordionContent> {...field}
</AccordionItem> reSize="none"
</Accordion> rows={3}
className="border-mineshaft-600 bg-mineshaft-900 text-sm"
/>
</FormControl>
)}
/>
<Controller
control={control}
name="provider.revocationStatement"
render={({ field, fieldState: { error } }) => (
<FormControl
label="Revocation Statement"
isError={Boolean(error?.message)}
errorText={error?.message}
helperText="username is dynamically provisioned"
>
<TextArea
{...field}
reSize="none"
rows={3}
className="border-mineshaft-600 bg-mineshaft-900 text-sm"
/>
</FormControl>
)}
/>
<Controller
control={control}
name="provider.renewStatement"
render={({ field, fieldState: { error } }) => (
<FormControl
label="Renew Statement"
helperText="username and expiration are dynamically provisioned"
isError={Boolean(error?.message)}
errorText={error?.message}
>
<TextArea
{...field}
reSize="none"
rows={3}
className="border-mineshaft-600 bg-mineshaft-900 text-sm"
/>
</FormControl>
)}
/>
</AccordionContent>
</AccordionItem>
</Accordion>
</div>
</div> </div>
</div> </div>
</div> </div>
@@ -118,13 +118,13 @@ export const CreateDynamicSecretLease = ({
}); });
createNotification({ createNotification({
type: "success", type: "success",
text: "Successfully created dynamic secret" text: "Successfully leased dynamic secret"
}); });
} catch (error) { } catch (error) {
console.log(error); console.log(error);
createNotification({ createNotification({
type: "error", type: "error",
text: "Failed to create dynamic secret" text: "Failed to lease dynamic secret"
}); });
} }
}; };
@@ -116,7 +116,7 @@ export const EditDynamicSecretSqlProviderForm = ({
onClose(); onClose();
createNotification({ createNotification({
type: "success", type: "success",
text: "Successfully update dynamic secret" text: "Successfully updated dynamic secret"
}); });
} catch (err) { } catch (err) {
createNotification({ createNotification({