review fixes + lint

This commit is contained in:
x032205
2025-10-03 01:46:28 -04:00
parent 6d74c1815a
commit 7d07e3a9a6
16 changed files with 212 additions and 241 deletions
@@ -5,7 +5,6 @@ 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 { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors"; import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
import { groupBy } from "@app/lib/fn"; import { groupBy } from "@app/lib/fn";
import { TOrgMembershipDALFactory } from "@app/services/org-membership/org-membership-dal";
import { TProjectDALFactory } from "@app/services/project/project-dal"; import { TProjectDALFactory } from "@app/services/project/project-dal";
import { TProjectEnvDALFactory } from "@app/services/project-env/project-env-dal"; import { TProjectEnvDALFactory } from "@app/services/project-env/project-env-dal";
import { TProjectMembershipDALFactory } from "@app/services/project-membership/project-membership-dal"; import { TProjectMembershipDALFactory } from "@app/services/project-membership/project-membership-dal";
@@ -45,7 +44,6 @@ type TAccessApprovalPolicyServiceFactoryDep = {
accessApprovalRequestDAL: Pick<TAccessApprovalRequestDALFactory, "update" | "find" | "resetReviewByPolicyId">; accessApprovalRequestDAL: Pick<TAccessApprovalRequestDALFactory, "update" | "find" | "resetReviewByPolicyId">;
additionalPrivilegeDAL: Pick<TProjectUserAdditionalPrivilegeDALFactory, "delete">; additionalPrivilegeDAL: Pick<TProjectUserAdditionalPrivilegeDALFactory, "delete">;
accessApprovalRequestReviewerDAL: Pick<TAccessApprovalRequestReviewerDALFactory, "update" | "delete">; accessApprovalRequestReviewerDAL: Pick<TAccessApprovalRequestReviewerDALFactory, "update" | "delete">;
orgMembershipDAL: Pick<TOrgMembershipDALFactory, "find">;
accessApprovalPolicyEnvironmentDAL: TAccessApprovalPolicyEnvironmentDALFactory; accessApprovalPolicyEnvironmentDAL: TAccessApprovalPolicyEnvironmentDALFactory;
}; };
@@ -62,7 +60,7 @@ export const accessApprovalPolicyServiceFactory = ({
accessApprovalRequestDAL, accessApprovalRequestDAL,
additionalPrivilegeDAL, additionalPrivilegeDAL,
accessApprovalRequestReviewerDAL, accessApprovalRequestReviewerDAL,
orgMembershipDAL projectMembershipDAL
}: TAccessApprovalPolicyServiceFactoryDep): TAccessApprovalPolicyServiceFactory => { }: TAccessApprovalPolicyServiceFactoryDep): TAccessApprovalPolicyServiceFactory => {
const $policyExists = async ({ const $policyExists = async ({
envId, envId,
@@ -85,6 +83,22 @@ export const accessApprovalPolicyServiceFactory = ({
return policyId ? policy && policy.id !== policyId : Boolean(policy); return policyId ? policy && policy.id !== policyId : Boolean(policy);
}; };
const verifyProjectUserMembership = async (userIds: string[], projectId: string) => {
if (userIds.length === 0) return;
const projectMemberships = await projectMembershipDAL.find({
$in: { userId: userIds },
projectId
});
if (projectMemberships.length !== userIds.length) {
const projectMemberUserIds = new Set(projectMemberships.map((member) => member.userId));
const userIdsNotInProject = userIds.filter((id) => !projectMemberUserIds.has(id));
throw new BadRequestError({
message: `Some users are not members of the project: ${userIdsNotInProject.join(", ")}`
});
}
};
const createAccessApprovalPolicy: TAccessApprovalPolicyServiceFactory["createAccessApprovalPolicy"] = async ({ const createAccessApprovalPolicy: TAccessApprovalPolicyServiceFactory["createAccessApprovalPolicy"] = async ({
name, name,
actor, actor,
@@ -175,19 +189,10 @@ export const accessApprovalPolicyServiceFactory = ({
} }
if (approverUserIds.length > 0) { if (approverUserIds.length > 0) {
const allApproverUserIds = approverUserIds.map((au) => au.id); await verifyProjectUserMembership(
const approverMembers = await orgMembershipDAL.find({ approverUserIds.map((au) => au.id),
$in: { userId: allApproverUserIds }, project.id
orgId: actorOrgId );
});
if (approverMembers.length !== allApproverUserIds.length) {
const approverMemberUserIds = new Set(approverMembers.map((member) => member.userId as string));
const userIdsNotInOrg = allApproverUserIds.filter((id) => !approverMemberUserIds.has(id));
throw new BadRequestError({
message: `Some approvers are not in the organization: ${userIdsNotInOrg.join(", ")}`
});
}
} }
let groupBypassers: string[] = []; let groupBypassers: string[] = [];
let bypasserUserIds: string[] = []; let bypasserUserIds: string[] = [];
@@ -440,18 +445,7 @@ export const accessApprovalPolicyServiceFactory = ({
// Validate user bypassers // Validate user bypassers
if (bypasserUserIds.length > 0) { if (bypasserUserIds.length > 0) {
const orgMemberships = await orgMembershipDAL.find({ await verifyProjectUserMembership(bypasserUserIds, accessApprovalPolicy.projectId);
$in: { userId: bypasserUserIds },
orgId: actorOrgId
});
if (orgMemberships.length !== bypasserUserIds.length) {
const foundUserIdsInOrg = new Set(orgMemberships.map((mem) => mem.userId));
const missingUserIds = bypasserUserIds.filter((id) => !foundUserIdsInOrg.has(id));
throw new BadRequestError({
message: `One or more specified bypasser users are not part of the organization or do not exist. Invalid or non-member user IDs: ${missingUserIds.join(", ")}`
});
}
} }
// Validate group bypassers // Validate group bypassers
@@ -517,19 +511,10 @@ export const accessApprovalPolicyServiceFactory = ({
} }
if (approverUserIds.length > 0) { if (approverUserIds.length > 0) {
const allApproverUserIds = approverUserIds.map((au) => au.id); await verifyProjectUserMembership(
const approverMembers = await orgMembershipDAL.find({ approverUserIds.map((au) => au.id),
$in: { userId: allApproverUserIds }, accessApprovalPolicy.projectId
orgId: actorOrgId );
});
if (approverMembers.length !== allApproverUserIds.length) {
const approverMemberUserIds = new Set(approverMembers.map((member) => member.userId as string));
const userIdsNotInOrg = allApproverUserIds.filter((id) => !approverMemberUserIds.has(id));
throw new BadRequestError({
message: `Some approvers are not in the organization: ${userIdsNotInOrg.join(", ")}`
});
}
} }
await accessApprovalPolicyApproverDAL.insertMany( await accessApprovalPolicyApproverDAL.insertMany(
approverUserIds.map((el) => ({ approverUserIds.map((el) => ({
@@ -7,8 +7,8 @@ import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services
import { BadRequestError, NotFoundError } from "@app/lib/errors"; import { BadRequestError, NotFoundError } from "@app/lib/errors";
import { removeTrailingSlash } from "@app/lib/fn"; import { removeTrailingSlash } from "@app/lib/fn";
import { containsGlobPatterns } from "@app/lib/picomatch"; import { containsGlobPatterns } from "@app/lib/picomatch";
import { TOrgMembershipDALFactory } from "@app/services/org-membership/org-membership-dal";
import { TProjectEnvDALFactory } from "@app/services/project-env/project-env-dal"; import { TProjectEnvDALFactory } from "@app/services/project-env/project-env-dal";
import { TProjectMembershipDALFactory } from "@app/services/project-membership/project-membership-dal";
import { TUserDALFactory } from "@app/services/user/user-dal"; import { TUserDALFactory } from "@app/services/user/user-dal";
import { ApproverType, BypasserType } from "../access-approval-policy/access-approval-policy-types"; import { ApproverType, BypasserType } from "../access-approval-policy/access-approval-policy-types";
@@ -40,7 +40,7 @@ type TSecretApprovalPolicyServiceFactoryDep = {
secretApprovalPolicyDAL: TSecretApprovalPolicyDALFactory; secretApprovalPolicyDAL: TSecretApprovalPolicyDALFactory;
projectEnvDAL: Pick<TProjectEnvDALFactory, "findOne" | "find">; projectEnvDAL: Pick<TProjectEnvDALFactory, "findOne" | "find">;
userDAL: Pick<TUserDALFactory, "find">; userDAL: Pick<TUserDALFactory, "find">;
orgMembershipDAL: Pick<TOrgMembershipDALFactory, "find">; projectMembershipDAL: Pick<TProjectMembershipDALFactory, "find">;
secretApprovalPolicyApproverDAL: TSecretApprovalPolicyApproverDALFactory; secretApprovalPolicyApproverDAL: TSecretApprovalPolicyApproverDALFactory;
secretApprovalPolicyBypasserDAL: TSecretApprovalPolicyBypasserDALFactory; secretApprovalPolicyBypasserDAL: TSecretApprovalPolicyBypasserDALFactory;
licenseService: Pick<TLicenseServiceFactory, "getPlan">; licenseService: Pick<TLicenseServiceFactory, "getPlan">;
@@ -58,10 +58,26 @@ export const secretApprovalPolicyServiceFactory = ({
secretApprovalPolicyEnvironmentDAL, secretApprovalPolicyEnvironmentDAL,
projectEnvDAL, projectEnvDAL,
userDAL, userDAL,
orgMembershipDAL, projectMembershipDAL,
licenseService, licenseService,
secretApprovalRequestDAL secretApprovalRequestDAL
}: TSecretApprovalPolicyServiceFactoryDep) => { }: TSecretApprovalPolicyServiceFactoryDep) => {
const verifyProjectUserMembership = async (userIds: string[], projectId: string) => {
if (userIds.length === 0) return;
const projectMemberships = await projectMembershipDAL.find({
$in: { userId: userIds },
projectId
});
if (projectMemberships.length !== userIds.length) {
const projectMemberUserIds = new Set(projectMemberships.map((member) => member.userId));
const userIdsNotInProject = userIds.filter((id) => !projectMemberUserIds.has(id));
throw new BadRequestError({
message: `Some users are not members of the project: ${userIdsNotInProject.join(", ")}`
});
}
};
const $policyExists = async ({ const $policyExists = async ({
envIds, envIds,
envId, envId,
@@ -205,6 +221,7 @@ export const secretApprovalPolicyServiceFactory = ({
}, },
tx tx
); );
await secretApprovalPolicyEnvironmentDAL.insertMany( await secretApprovalPolicyEnvironmentDAL.insertMany(
envs.map((env) => ({ envs.map((env) => ({
envId: env.id, envId: env.id,
@@ -236,23 +253,7 @@ export const secretApprovalPolicyServiceFactory = ({
userApproverIds = userApproverIds.concat(approverUsers.map((user) => user.id)); userApproverIds = userApproverIds.concat(approverUsers.map((user) => user.id));
} }
if (userApproverIds.length > 0) { await verifyProjectUserMembership(userApproverIds, projectId);
const approverMembers = await orgMembershipDAL.find(
{
$in: { userId: userApproverIds },
orgId: actorOrgId
},
{ tx }
);
if (approverMembers.length !== userApproverIds.length) {
const approverMemberUserIds = new Set(approverMembers.map((member) => member.userId as string));
const userIdsNotInOrg = userApproverIds.filter((id) => !approverMemberUserIds.has(id));
throw new BadRequestError({
message: `Some approvers are not in the organization: ${userIdsNotInOrg.join(", ")}`
});
}
}
await secretApprovalPolicyApproverDAL.insertMany( await secretApprovalPolicyApproverDAL.insertMany(
userApproverIds.map((approverUserId) => ({ userApproverIds.map((approverUserId) => ({
@@ -446,23 +447,7 @@ export const secretApprovalPolicyServiceFactory = ({
userApproverIds = userApproverIds.concat(approverUsers.map((user) => user.id)); userApproverIds = userApproverIds.concat(approverUsers.map((user) => user.id));
} }
if (userApproverIds.length > 0) { await verifyProjectUserMembership(userApproverIds, secretApprovalPolicy.projectId);
const approverMembers = await orgMembershipDAL.find(
{
$in: { userId: userApproverIds },
orgId: actorOrgId
},
{ tx }
);
if (approverMembers.length !== userApproverIds.length) {
const approverMemberUserIds = new Set(approverMembers.map((member) => member.userId as string));
const userIdsNotInOrg = userApproverIds.filter((id) => !approverMemberUserIds.has(id));
throw new BadRequestError({
message: `Some approvers are not in the organization: ${userIdsNotInOrg.join(", ")}`
});
}
}
await secretApprovalPolicyApproverDAL.insertMany( await secretApprovalPolicyApproverDAL.insertMany(
userApproverIds.map((approverUserId) => ({ userApproverIds.map((approverUserId) => ({
+2 -3
View File
@@ -611,7 +611,7 @@ export const registerRoutes = async (
secretApprovalPolicyDAL, secretApprovalPolicyDAL,
licenseService, licenseService,
userDAL, userDAL,
orgMembershipDAL, projectMembershipDAL,
secretApprovalRequestDAL secretApprovalRequestDAL
}); });
const tokenService = tokenServiceFactory({ tokenDAL: authTokenDAL, userDAL, orgMembershipDAL }); const tokenService = tokenServiceFactory({ tokenDAL: authTokenDAL, userDAL, orgMembershipDAL });
@@ -1421,8 +1421,7 @@ export const registerRoutes = async (
userDAL, userDAL,
accessApprovalRequestDAL, accessApprovalRequestDAL,
additionalPrivilegeDAL: projectUserAdditionalPrivilegeDAL, additionalPrivilegeDAL: projectUserAdditionalPrivilegeDAL,
accessApprovalRequestReviewerDAL, accessApprovalRequestReviewerDAL
orgMembershipDAL
}); });
const accessApprovalRequestService = accessApprovalRequestServiceFactory({ const accessApprovalRequestService = accessApprovalRequestServiceFactory({
+1 -1
View File
@@ -58,8 +58,8 @@ import { registerSecretRequestsRouter } from "./secret-requests-router";
import { registerSecretSharingRouter } from "./secret-sharing-router"; import { registerSecretSharingRouter } from "./secret-sharing-router";
import { registerSecretTagRouter } from "./secret-tag-router"; import { registerSecretTagRouter } from "./secret-tag-router";
import { registerSlackRouter } from "./slack-router"; import { registerSlackRouter } from "./slack-router";
import { registerUpgradePathRouter } from "./upgrade-path-router";
import { registerSsoRouter } from "./sso-router"; import { registerSsoRouter } from "./sso-router";
import { registerUpgradePathRouter } from "./upgrade-path-router";
import { registerUserActionRouter } from "./user-action-router"; import { registerUserActionRouter } from "./user-action-router";
import { registerUserEngagementRouter } from "./user-engagement-router"; import { registerUserEngagementRouter } from "./user-engagement-router";
import { registerUserRouter } from "./user-router"; import { registerUserRouter } from "./user-router";
@@ -1,7 +1,7 @@
import { CredentialDisplay } from "@app/components/secret-rotations-v2/ViewSecretRotationV2GeneratedCredentials/shared/CredentialDisplay"; import { CredentialDisplay } from "@app/components/secret-rotations-v2/ViewSecretRotationV2GeneratedCredentials/shared/CredentialDisplay";
import { TRedisCredentialsRotationGeneratedCredentialsResponse } from "@app/hooks/api/secretRotationsV2/types/redis-credentials-rotation";
import { ViewRotationGeneratedCredentialsDisplay } from "./shared"; import { ViewRotationGeneratedCredentialsDisplay } from "./shared";
import { TRedisCredentialsRotationGeneratedCredentialsResponse } from "@app/hooks/api/secretRotationsV2/types/redis-credentials-rotation";
type Props = { type Props = {
generatedCredentialsResponse: TRedisCredentialsRotationGeneratedCredentialsResponse; generatedCredentialsResponse: TRedisCredentialsRotationGeneratedCredentialsResponse;
@@ -3,6 +3,7 @@ import { Controller, useFormContext } from "react-hook-form";
import { TSecretRotationV2Form } from "@app/components/secret-rotations-v2/forms/schemas"; import { TSecretRotationV2Form } from "@app/components/secret-rotations-v2/forms/schemas";
import { FormControl, Input } from "@app/components/v2"; import { FormControl, Input } from "@app/components/v2";
import { SecretRotation } from "@app/hooks/api/secretRotationsV2"; import { SecretRotation } from "@app/hooks/api/secretRotationsV2";
import { DEFAULT_PASSWORD_REQUIREMENTS } from "../schemas/shared"; import { DEFAULT_PASSWORD_REQUIREMENTS } from "../schemas/shared";
export const RedisCredentialsRotationParametersFields = () => { export const RedisCredentialsRotationParametersFields = () => {
@@ -18,7 +19,7 @@ export const RedisCredentialsRotationParametersFields = () => {
<Controller <Controller
control={control} control={control}
name="parameters.permissionScope" name="parameters.permissionScope"
defaultValue={""} defaultValue=""
render={({ field, fieldState: { error } }) => ( render={({ field, fieldState: { error } }) => (
<FormControl <FormControl
tooltipClassName="max-w-[40rem] w-full" tooltipClassName="max-w-[40rem] w-full"
@@ -8,8 +8,8 @@ import { AwsIamUserSecretRotationParametersFields } from "./AwsIamUserSecretRota
import { AzureClientSecretRotationParametersFields } from "./AzureClientSecretRotationParametersFields"; import { AzureClientSecretRotationParametersFields } from "./AzureClientSecretRotationParametersFields";
import { LdapPasswordRotationParametersFields } from "./LdapPasswordRotationParametersFields"; import { LdapPasswordRotationParametersFields } from "./LdapPasswordRotationParametersFields";
import { OktaClientSecretRotationParametersFields } from "./OktaClientSecretRotationParametersFields"; import { OktaClientSecretRotationParametersFields } from "./OktaClientSecretRotationParametersFields";
import { SqlCredentialsRotationParametersFields } from "./shared";
import { RedisCredentialsRotationParametersFields } from "./RedisCredentialsRotationParametersFields"; import { RedisCredentialsRotationParametersFields } from "./RedisCredentialsRotationParametersFields";
import { SqlCredentialsRotationParametersFields } from "./shared";
const COMPONENT_MAP: Record<SecretRotation, React.FC> = { const COMPONENT_MAP: Record<SecretRotation, React.FC> = {
[SecretRotation.PostgresCredentials]: SqlCredentialsRotationParametersFields, [SecretRotation.PostgresCredentials]: SqlCredentialsRotationParametersFields,
@@ -11,8 +11,8 @@ import { AwsIamUserSecretRotationReviewFields } from "./AwsIamUserSecretRotation
import { AzureClientSecretRotationReviewFields } from "./AzureClientSecretRotationReviewFields"; import { AzureClientSecretRotationReviewFields } from "./AzureClientSecretRotationReviewFields";
import { LdapPasswordRotationReviewFields } from "./LdapPasswordRotationReviewFields"; import { LdapPasswordRotationReviewFields } from "./LdapPasswordRotationReviewFields";
import { OktaClientSecretRotationReviewFields } from "./OktaClientSecretRotationReviewFields"; import { OktaClientSecretRotationReviewFields } from "./OktaClientSecretRotationReviewFields";
import { SqlCredentialsRotationReviewFields } from "./shared";
import { RedisCredentialsRotationReviewFields } from "./RedisCredentialsRotationReviewFields"; import { RedisCredentialsRotationReviewFields } from "./RedisCredentialsRotationReviewFields";
import { SqlCredentialsRotationReviewFields } from "./shared";
const COMPONENT_MAP: Record<SecretRotation, React.FC> = { const COMPONENT_MAP: Record<SecretRotation, React.FC> = {
[SecretRotation.PostgresCredentials]: SqlCredentialsRotationReviewFields, [SecretRotation.PostgresCredentials]: SqlCredentialsRotationReviewFields,
@@ -8,8 +8,8 @@ import { AwsIamUserSecretRotationSecretsMappingFields } from "./AwsIamUserSecret
import { AzureClientSecretRotationSecretsMappingFields } from "./AzureClientSecretRotationSecretsMappingFields"; import { AzureClientSecretRotationSecretsMappingFields } from "./AzureClientSecretRotationSecretsMappingFields";
import { LdapPasswordRotationSecretsMappingFields } from "./LdapPasswordRotationSecretsMappingFields"; import { LdapPasswordRotationSecretsMappingFields } from "./LdapPasswordRotationSecretsMappingFields";
import { OktaClientSecretRotationSecretsMappingFields } from "./OktaClientSecretRotationSecretsMappingFields"; import { OktaClientSecretRotationSecretsMappingFields } from "./OktaClientSecretRotationSecretsMappingFields";
import { SqlCredentialsRotationSecretsMappingFields } from "./shared";
import { RedisCredentialsRotationSecretsMappingFields } from "./RedisCredentialsRotationSecretsMappingFields"; import { RedisCredentialsRotationSecretsMappingFields } from "./RedisCredentialsRotationSecretsMappingFields";
import { SqlCredentialsRotationSecretsMappingFields } from "./shared";
const COMPONENT_MAP: Record<SecretRotation, React.FC> = { const COMPONENT_MAP: Record<SecretRotation, React.FC> = {
[SecretRotation.PostgresCredentials]: SqlCredentialsRotationSecretsMappingFields, [SecretRotation.PostgresCredentials]: SqlCredentialsRotationSecretsMappingFields,
@@ -164,7 +164,10 @@ export const PkiSyncRow = ({
</div> </div>
</Td> </Td>
{subscriberId ? ( {subscriberId ? (
<PkiSyncTableCell primaryText={pkiSync.subscriber?.name || subscriberId} secondaryText="PKI Subscriber" /> <PkiSyncTableCell
primaryText={pkiSync.subscriber?.name || subscriberId}
secondaryText="PKI Subscriber"
/>
) : ( ) : (
<Td> <Td>
<Tooltip content="The PKI subscriber for this sync has been deleted. Configure a new source or remove this sync."> <Tooltip content="The PKI subscriber for this sync has been deleted. Configure a new source or remove this sync.">
@@ -40,6 +40,7 @@ import { OktaConnectionForm } from "./OktaConnectionForm";
import { OracleDBConnectionForm } from "./OracleDBConnectionForm"; import { OracleDBConnectionForm } from "./OracleDBConnectionForm";
import { PostgresConnectionForm } from "./PostgresConnectionForm"; import { PostgresConnectionForm } from "./PostgresConnectionForm";
import { RailwayConnectionForm } from "./RailwayConnectionForm"; import { RailwayConnectionForm } from "./RailwayConnectionForm";
import { RedisConnectionForm } from "./RedisConnectionForm";
import { RenderConnectionForm } from "./RenderConnectionForm"; import { RenderConnectionForm } from "./RenderConnectionForm";
import { SupabaseConnectionForm } from "./SupabaseConnectionForm"; import { SupabaseConnectionForm } from "./SupabaseConnectionForm";
import { TeamCityConnectionForm } from "./TeamCityConnectionForm"; import { TeamCityConnectionForm } from "./TeamCityConnectionForm";
@@ -47,7 +48,6 @@ import { TerraformCloudConnectionForm } from "./TerraformCloudConnectionForm";
import { VercelConnectionForm } from "./VercelConnectionForm"; import { VercelConnectionForm } from "./VercelConnectionForm";
import { WindmillConnectionForm } from "./WindmillConnectionForm"; import { WindmillConnectionForm } from "./WindmillConnectionForm";
import { ZabbixConnectionForm } from "./ZabbixConnectionForm"; import { ZabbixConnectionForm } from "./ZabbixConnectionForm";
import { RedisConnectionForm } from "./RedisConnectionForm";
type FormProps = { type FormProps = {
onComplete: (appConnection: TAppConnection) => void; onComplete: (appConnection: TAppConnection) => void;
@@ -1,9 +1,11 @@
import { useState } from "react"; import { useState } from "react";
import { Controller, FormProvider, useForm } from "react-hook-form"; import { Controller, FormProvider, useForm } from "react-hook-form";
import { faQuestionCircle } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Tab } from "@headlessui/react";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod"; import { z } from "zod";
import { Tab } from "@headlessui/react";
import { import {
Button, Button,
FormControl, FormControl,
@@ -24,8 +26,6 @@ import {
genericAppConnectionFieldsSchema, genericAppConnectionFieldsSchema,
GenericAppConnectionsFields GenericAppConnectionsFields
} from "./GenericAppConnectionFields"; } from "./GenericAppConnectionFields";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faQuestionCircle } from "@fortawesome/free-solid-svg-icons";
type Props = { type Props = {
appConnection?: TRedisConnection; appConnection?: TRedisConnection;
@@ -123,175 +123,173 @@ export const RedisConnectionForm = ({ appConnection, onSubmit }: Props) => {
)} )}
/> />
<> <Tab.Group selectedIndex={selectedTabIndex} onChange={setSelectedTabIndex}>
<Tab.Group selectedIndex={selectedTabIndex} onChange={setSelectedTabIndex}> <Tab.List className="-pb-1 mb-6 w-full border-b-2 border-mineshaft-600">
<Tab.List className="-pb-1 mb-6 w-full border-b-2 border-mineshaft-600"> <Tab
<Tab className={({ selected }) =>
className={({ selected }) => `w-30 -mb-[0.14rem] px-4 py-2 text-sm font-medium outline-none disabled:opacity-60 ${
`w-30 -mb-[0.14rem] px-4 py-2 text-sm font-medium outline-none disabled:opacity-60 ${ selected
selected ? "border-b-2 border-mineshaft-300 text-mineshaft-200"
? "border-b-2 border-mineshaft-300 text-mineshaft-200" : "text-bunker-300"
: "text-bunker-300" }`
}` }
} >
> Configuration
Configuration </Tab>
</Tab> <Tab
<Tab className={({ selected }) =>
className={({ selected }) => `w-30 -mb-[0.14rem] px-4 py-2 text-sm font-medium outline-none disabled:opacity-60 ${
`w-30 -mb-[0.14rem] px-4 py-2 text-sm font-medium outline-none disabled:opacity-60 ${ selected
selected ? "border-b-2 border-mineshaft-300 text-mineshaft-200"
? "border-b-2 border-mineshaft-300 text-mineshaft-200" : "text-bunker-300"
: "text-bunker-300" }`
}` }
} >
> SSL ({sslEnabled ? "Enabled" : "Disabled"})
SSL ({sslEnabled ? "Enabled" : "Disabled"}) </Tab>
</Tab> </Tab.List>
</Tab.List> <Tab.Panels className="mb-4 rounded border border-mineshaft-600 bg-mineshaft-700/70 p-3 pb-0">
<Tab.Panels className="mb-4 rounded border border-mineshaft-600 bg-mineshaft-700/70 p-3 pb-0"> <Tab.Panel>
<Tab.Panel> <div className="mt-[0.675rem] flex items-start gap-2">
<div className="mt-[0.675rem] flex items-start gap-2">
<Controller
name="credentials.host"
control={control}
render={({ field, fieldState: { error } }) => (
<FormControl
className="flex-1"
errorText={error?.message}
isError={Boolean(error?.message)}
label="Host"
>
<Input {...field} />
</FormControl>
)}
/>
<Controller
name="credentials.port"
control={control}
render={({ field, fieldState: { error } }) => (
<FormControl
className="w-28"
errorText={error?.message}
isError={Boolean(error?.message)}
label="Port"
>
<Input type="number" {...field} />
</FormControl>
)}
/>
</div>
<div className="mb-[0.675rem] flex items-start gap-2">
<Controller
name="credentials.username"
control={control}
render={({ field, fieldState: { error } }) => (
<FormControl
errorText={error?.message}
isError={Boolean(error?.message)}
label="Username"
className="flex-1"
>
<Input {...field} />
</FormControl>
)}
/>
<Controller
name="credentials.password"
control={control}
render={({ field: { value, onChange }, fieldState: { error } }) => (
<FormControl
errorText={error?.message}
isError={Boolean(error?.message)}
label="Password"
className="flex-1"
>
<SecretInput
containerClassName="text-gray-400 w-full group-focus-within:!border-primary-400/50 border border-mineshaft-500 bg-mineshaft-900 px-2.5 py-1.5"
value={value}
onChange={(e) => onChange(e.target.value)}
/>
</FormControl>
)}
/>
</div>
</Tab.Panel>
<Tab.Panel>
<Controller <Controller
name="credentials.sslEnabled" name="credentials.host"
control={control} control={control}
render={({ field: { value, onChange }, fieldState: { error } }) => ( render={({ field, fieldState: { error } }) => (
<FormControl isError={Boolean(error?.message)} errorText={error?.message}> <FormControl
<Switch className="flex-1"
className="bg-mineshaft-400/50 shadow-inner data-[state=checked]:bg-green/80" errorText={error?.message}
id="ssl-enabled" isError={Boolean(error?.message)}
thumbClassName="bg-mineshaft-800" label="Host"
isChecked={value} >
onCheckedChange={onChange} <Input {...field} />
>
Enable SSL
</Switch>
</FormControl> </FormControl>
)} )}
/> />
<Controller <Controller
name="credentials.sslCertificate" name="credentials.port"
control={control}
render={({ field, fieldState: { error } }) => (
<FormControl
className="w-28"
errorText={error?.message}
isError={Boolean(error?.message)}
label="Port"
>
<Input type="number" {...field} />
</FormControl>
)}
/>
</div>
<div className="mb-[0.675rem] flex items-start gap-2">
<Controller
name="credentials.username"
control={control} control={control}
render={({ field, fieldState: { error } }) => ( render={({ field, fieldState: { error } }) => (
<FormControl <FormControl
errorText={error?.message} errorText={error?.message}
isError={Boolean(error?.message)} isError={Boolean(error?.message)}
className={sslEnabled ? "" : "opacity-50"} label="Username"
label="SSL Certificate" className="flex-1"
isOptional
> >
<TextArea <Input {...field} />
className="h-[3.5rem] !resize-none"
{...field}
isDisabled={!sslEnabled}
/>
</FormControl> </FormControl>
)} )}
/> />
<Controller <Controller
name="credentials.sslRejectUnauthorized" name="credentials.password"
control={control} control={control}
render={({ field: { value, onChange }, fieldState: { error } }) => ( render={({ field: { value, onChange }, fieldState: { error } }) => (
<FormControl <FormControl
className={sslEnabled ? "" : "opacity-50"}
isError={Boolean(error?.message)}
errorText={error?.message} errorText={error?.message}
isError={Boolean(error?.message)}
label="Password"
className="flex-1"
> >
<Switch <SecretInput
className="bg-mineshaft-400/50 shadow-inner data-[state=checked]:bg-green/80" containerClassName="text-gray-400 w-full group-focus-within:!border-primary-400/50 border border-mineshaft-500 bg-mineshaft-900 px-2.5 py-1.5"
id="ssl-reject-unauthorized" value={value}
thumbClassName="bg-mineshaft-800" onChange={(e) => onChange(e.target.value)}
isChecked={sslEnabled ? value : false} />
onCheckedChange={onChange}
isDisabled={!sslEnabled}
>
<p className="w-[9.5rem]">
Reject Unauthorized
<Tooltip
className="max-w-md"
content={
<p>
If enabled, Infisical will only connect to the server if it has a
valid, trusted SSL certificate.
</p>
}
>
<FontAwesomeIcon icon={faQuestionCircle} size="sm" className="ml-1" />
</Tooltip>
</p>
</Switch>
</FormControl> </FormControl>
)} )}
/> />
</Tab.Panel> </div>
</Tab.Panels> </Tab.Panel>
</Tab.Group> <Tab.Panel>
</> <Controller
name="credentials.sslEnabled"
control={control}
render={({ field: { value, onChange }, fieldState: { error } }) => (
<FormControl isError={Boolean(error?.message)} errorText={error?.message}>
<Switch
className="bg-mineshaft-400/50 shadow-inner data-[state=checked]:bg-green/80"
id="ssl-enabled"
thumbClassName="bg-mineshaft-800"
isChecked={value}
onCheckedChange={onChange}
>
Enable SSL
</Switch>
</FormControl>
)}
/>
<Controller
name="credentials.sslCertificate"
control={control}
render={({ field, fieldState: { error } }) => (
<FormControl
errorText={error?.message}
isError={Boolean(error?.message)}
className={sslEnabled ? "" : "opacity-50"}
label="SSL Certificate"
isOptional
>
<TextArea
className="h-[3.5rem] !resize-none"
{...field}
isDisabled={!sslEnabled}
/>
</FormControl>
)}
/>
<Controller
name="credentials.sslRejectUnauthorized"
control={control}
render={({ field: { value, onChange }, fieldState: { error } }) => (
<FormControl
className={sslEnabled ? "" : "opacity-50"}
isError={Boolean(error?.message)}
errorText={error?.message}
>
<Switch
className="bg-mineshaft-400/50 shadow-inner data-[state=checked]:bg-green/80"
id="ssl-reject-unauthorized"
thumbClassName="bg-mineshaft-800"
isChecked={sslEnabled ? value : false}
onCheckedChange={onChange}
isDisabled={!sslEnabled}
>
<p className="w-[9.5rem]">
Reject Unauthorized
<Tooltip
className="max-w-md"
content={
<p>
If enabled, Infisical will only connect to the server if it has a
valid, trusted SSL certificate.
</p>
}
>
<FontAwesomeIcon icon={faQuestionCircle} size="sm" className="ml-1" />
</Tooltip>
</p>
</Switch>
</FormControl>
)}
/>
</Tab.Panel>
</Tab.Panels>
</Tab.Group>
<div className="mt-6 flex items-center"> <div className="mt-6 flex items-center">
<Button <Button
@@ -221,7 +221,7 @@ export const CreateSecretForm = ({ secretPath = "/", onClose }: Props) => {
warning={ warning={
secretKey?.includes(" ") ? ( secretKey?.includes(" ") ? (
<Tooltip <Tooltip
className={"w-full max-w-72"} className="w-full max-w-72"
content={ content={
<div> <div>
Secret key contains whitespaces. Secret key contains whitespaces.
@@ -158,7 +158,7 @@ function SecretRenameRow({ environments, getSecretByKey, secretKey, secretPath }
{currentSecretValue?.trim()?.includes(" ") && {currentSecretValue?.trim()?.includes(" ") &&
currentSecretValue?.trim() !== secretKey && ( currentSecretValue?.trim() !== secretKey && (
<Tooltip <Tooltip
className={"w-full max-w-72"} className="w-full max-w-72"
content={ content={
<div> <div>
Secret key contains whitespaces. Secret key contains whitespaces.
@@ -181,7 +181,7 @@ export const CreateSecretForm = ({
warning={ warning={
secretKey?.includes(" ") ? ( secretKey?.includes(" ") ? (
<Tooltip <Tooltip
className={"w-full max-w-72"} className="w-full max-w-72"
content={ content={
<div> <div>
Secret key contains whitespaces. Secret key contains whitespaces.
@@ -491,7 +491,7 @@ export const SecretItem = memo(
field?.value !== (originalSecret.originalKey || originalSecret.key) && field?.value !== (originalSecret.originalKey || originalSecret.key) &&
field.value?.includes(" ") ? ( field.value?.includes(" ") ? (
<Tooltip <Tooltip
className={"w-full max-w-72"} className="w-full max-w-72"
content={ content={
<div> <div>
Secret key contains whitespaces. Secret key contains whitespaces.