mirror of
https://github.com/Infisical/infisical.git
synced 2025-03-25 14:05:03 +00:00
Compare commits
1 Commits
main
...
daniel/sci
Author | SHA1 | Date | |
---|---|---|---|
4b9f409ea5 |
backend/src/ee/services
dynamic-secret/providers
scim
cli/packages/util
frontend
src
hooks
pages
organization/AccessManagementPage/components/OrgMembersTab/components/OrgMembersSection
project/AccessControlPage/components/MembersTab/components
secret-manager/OverviewPage
@ -21,12 +21,7 @@ const generateUsername = () => {
|
||||
export const CassandraProvider = (): TDynamicProviderFns => {
|
||||
const validateProviderInputs = async (inputs: unknown) => {
|
||||
const providerInputs = await DynamicSecretCassandraSchema.parseAsync(inputs);
|
||||
const hostIps = await Promise.all(
|
||||
providerInputs.host
|
||||
.split(",")
|
||||
.filter(Boolean)
|
||||
.map((el) => verifyHostInputValidity(el).then((ip) => ip[0]))
|
||||
);
|
||||
const [hostIp] = await verifyHostInputValidity(providerInputs.host);
|
||||
validateHandlebarTemplate("Cassandra creation", providerInputs.creationStatement, {
|
||||
allowedExpressions: (val) => ["username", "password", "expiration", "keyspace"].includes(val)
|
||||
});
|
||||
@ -39,10 +34,10 @@ export const CassandraProvider = (): TDynamicProviderFns => {
|
||||
allowedExpressions: (val) => ["username"].includes(val)
|
||||
});
|
||||
|
||||
return { ...providerInputs, hostIps };
|
||||
return { ...providerInputs, host: hostIp };
|
||||
};
|
||||
|
||||
const $getClient = async (providerInputs: z.infer<typeof DynamicSecretCassandraSchema> & { hostIps: string[] }) => {
|
||||
const $getClient = async (providerInputs: z.infer<typeof DynamicSecretCassandraSchema>) => {
|
||||
const sslOptions = providerInputs.ca ? { rejectUnauthorized: false, ca: providerInputs.ca } : undefined;
|
||||
const client = new cassandra.Client({
|
||||
sslOptions,
|
||||
@ -55,7 +50,7 @@ export const CassandraProvider = (): TDynamicProviderFns => {
|
||||
},
|
||||
keyspace: providerInputs.keyspace,
|
||||
localDataCenter: providerInputs?.localDataCenter,
|
||||
contactPoints: providerInputs.hostIps
|
||||
contactPoints: providerInputs.host.split(",").filter(Boolean)
|
||||
});
|
||||
return client;
|
||||
};
|
||||
|
@ -20,13 +20,13 @@ export const ElasticSearchProvider = (): TDynamicProviderFns => {
|
||||
const validateProviderInputs = async (inputs: unknown) => {
|
||||
const providerInputs = await DynamicSecretElasticSearchSchema.parseAsync(inputs);
|
||||
const [hostIp] = await verifyHostInputValidity(providerInputs.host);
|
||||
return { ...providerInputs, hostIp };
|
||||
return { ...providerInputs, host: hostIp };
|
||||
};
|
||||
|
||||
const $getClient = async (providerInputs: z.infer<typeof DynamicSecretElasticSearchSchema> & { hostIp: string }) => {
|
||||
const $getClient = async (providerInputs: z.infer<typeof DynamicSecretElasticSearchSchema>) => {
|
||||
const connection = new ElasticSearchClient({
|
||||
node: {
|
||||
url: new URL(`${providerInputs.hostIp}:${providerInputs.port}`),
|
||||
url: new URL(`${providerInputs.host}:${providerInputs.port}`),
|
||||
...(providerInputs.ca && {
|
||||
ssl: {
|
||||
rejectUnauthorized: false,
|
||||
|
@ -20,14 +20,14 @@ export const MongoDBProvider = (): TDynamicProviderFns => {
|
||||
const validateProviderInputs = async (inputs: unknown) => {
|
||||
const providerInputs = await DynamicSecretMongoDBSchema.parseAsync(inputs);
|
||||
const [hostIp] = await verifyHostInputValidity(providerInputs.host);
|
||||
return { ...providerInputs, hostIp };
|
||||
return { ...providerInputs, host: hostIp };
|
||||
};
|
||||
|
||||
const $getClient = async (providerInputs: z.infer<typeof DynamicSecretMongoDBSchema> & { hostIp: string }) => {
|
||||
const $getClient = async (providerInputs: z.infer<typeof DynamicSecretMongoDBSchema>) => {
|
||||
const isSrv = !providerInputs.port;
|
||||
const uri = isSrv
|
||||
? `mongodb+srv://${providerInputs.hostIp}`
|
||||
: `mongodb://${providerInputs.hostIp}:${providerInputs.port}`;
|
||||
? `mongodb+srv://${providerInputs.host}`
|
||||
: `mongodb://${providerInputs.host}:${providerInputs.port}`;
|
||||
|
||||
const client = new MongoClient(uri, {
|
||||
auth: {
|
||||
|
@ -3,6 +3,7 @@ import https from "https";
|
||||
import { customAlphabet } from "nanoid";
|
||||
import { z } from "zod";
|
||||
|
||||
import { removeTrailingSlash } from "@app/lib/fn";
|
||||
import { logger } from "@app/lib/logger";
|
||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||
|
||||
@ -79,12 +80,12 @@ export const RabbitMqProvider = (): TDynamicProviderFns => {
|
||||
const validateProviderInputs = async (inputs: unknown) => {
|
||||
const providerInputs = await DynamicSecretRabbitMqSchema.parseAsync(inputs);
|
||||
const [hostIp] = await verifyHostInputValidity(providerInputs.host);
|
||||
return { ...providerInputs, hostIp };
|
||||
return { ...providerInputs, host: hostIp };
|
||||
};
|
||||
|
||||
const $getClient = async (providerInputs: z.infer<typeof DynamicSecretRabbitMqSchema> & { hostIp: string }) => {
|
||||
const $getClient = async (providerInputs: z.infer<typeof DynamicSecretRabbitMqSchema>) => {
|
||||
const axiosInstance = axios.create({
|
||||
baseURL: `${providerInputs.hostIp}:${providerInputs.port}/api`,
|
||||
baseURL: `${removeTrailingSlash(providerInputs.host)}:${providerInputs.port}/api`,
|
||||
auth: {
|
||||
username: providerInputs.username,
|
||||
password: providerInputs.password
|
||||
|
@ -65,15 +65,15 @@ export const RedisDatabaseProvider = (): TDynamicProviderFns => {
|
||||
allowedExpressions: (val) => ["username"].includes(val)
|
||||
});
|
||||
|
||||
return { ...providerInputs, hostIp };
|
||||
return { ...providerInputs, host: hostIp };
|
||||
};
|
||||
|
||||
const $getClient = async (providerInputs: z.infer<typeof DynamicSecretRedisDBSchema> & { hostIp: string }) => {
|
||||
const $getClient = async (providerInputs: z.infer<typeof DynamicSecretRedisDBSchema>) => {
|
||||
let connection: Redis | null = null;
|
||||
try {
|
||||
connection = new Redis({
|
||||
username: providerInputs.username,
|
||||
host: providerInputs.hostIp,
|
||||
host: providerInputs.host,
|
||||
port: providerInputs.port,
|
||||
password: providerInputs.password,
|
||||
...(providerInputs.ca && {
|
||||
|
@ -37,16 +37,13 @@ export const SapAseProvider = (): TDynamicProviderFns => {
|
||||
allowedExpressions: (val) => ["username"].includes(val)
|
||||
});
|
||||
}
|
||||
return { ...providerInputs, hostIp };
|
||||
return { ...providerInputs, host: hostIp };
|
||||
};
|
||||
|
||||
const $getClient = async (
|
||||
providerInputs: z.infer<typeof DynamicSecretSapAseSchema> & { hostIp: string },
|
||||
useMaster?: boolean
|
||||
) => {
|
||||
const $getClient = async (providerInputs: z.infer<typeof DynamicSecretSapAseSchema>, useMaster?: boolean) => {
|
||||
const connectionString =
|
||||
`DRIVER={FreeTDS};` +
|
||||
`SERVER=${providerInputs.hostIp};` +
|
||||
`SERVER=${providerInputs.host};` +
|
||||
`PORT=${providerInputs.port};` +
|
||||
`DATABASE=${useMaster ? "master" : providerInputs.database};` +
|
||||
`UID=${providerInputs.username};` +
|
||||
|
@ -41,12 +41,12 @@ export const SapHanaProvider = (): TDynamicProviderFns => {
|
||||
validateHandlebarTemplate("SAP Hana revoke", providerInputs.revocationStatement, {
|
||||
allowedExpressions: (val) => ["username"].includes(val)
|
||||
});
|
||||
return { ...providerInputs, hostIp };
|
||||
return { ...providerInputs, host: hostIp };
|
||||
};
|
||||
|
||||
const $getClient = async (providerInputs: z.infer<typeof DynamicSecretSapHanaSchema> & { hostIp: string }) => {
|
||||
const $getClient = async (providerInputs: z.infer<typeof DynamicSecretSapHanaSchema>) => {
|
||||
const client = hdb.createClient({
|
||||
host: providerInputs.hostIp,
|
||||
host: providerInputs.host,
|
||||
port: providerInputs.port,
|
||||
user: providerInputs.username,
|
||||
password: providerInputs.password,
|
||||
|
@ -132,7 +132,7 @@ export const SqlDatabaseProvider = ({ gatewayService }: TSqlDatabaseProviderDTO)
|
||||
allowedExpressions: (val) => ["username", "database"].includes(val)
|
||||
});
|
||||
|
||||
return { ...providerInputs, hostIp };
|
||||
return { ...providerInputs, host: hostIp };
|
||||
};
|
||||
|
||||
const $getClient = async (providerInputs: z.infer<typeof DynamicSecretSqlDBSchema>) => {
|
||||
@ -193,7 +193,7 @@ export const SqlDatabaseProvider = ({ gatewayService }: TSqlDatabaseProviderDTO)
|
||||
const validateConnection = async (inputs: unknown) => {
|
||||
const providerInputs = await validateProviderInputs(inputs);
|
||||
let isConnected = false;
|
||||
const gatewayCallback = async (host = providerInputs.hostIp, port = providerInputs.port) => {
|
||||
const gatewayCallback = async (host = providerInputs.host, port = providerInputs.port) => {
|
||||
const db = await $getClient({ ...providerInputs, port, host });
|
||||
// oracle needs from keyword
|
||||
const testStatement = providerInputs.client === SqlProviders.Oracle ? "SELECT 1 FROM DUAL" : "SELECT 1";
|
||||
|
@ -583,6 +583,8 @@ export const scimServiceFactory = ({
|
||||
|
||||
const serverCfg = await getServerCfg();
|
||||
await userDAL.transaction(async (tx) => {
|
||||
const { role, roleId } = await getDefaultOrgMembershipRole(org.defaultMembershipRole);
|
||||
|
||||
await userAliasDAL.update(
|
||||
{
|
||||
orgId,
|
||||
@ -594,10 +596,15 @@ export const scimServiceFactory = ({
|
||||
},
|
||||
tx
|
||||
);
|
||||
|
||||
await orgMembershipDAL.updateById(
|
||||
membership.id,
|
||||
{
|
||||
isActive: active
|
||||
isActive: active,
|
||||
...(active && {
|
||||
role,
|
||||
roleId
|
||||
})
|
||||
},
|
||||
tx
|
||||
);
|
||||
|
@ -56,7 +56,6 @@ func WriteInitalConfig(userCredentials *models.UserCredentials) error {
|
||||
LoggedInUsers: existingConfigFile.LoggedInUsers,
|
||||
VaultBackendType: existingConfigFile.VaultBackendType,
|
||||
VaultBackendPassphrase: existingConfigFile.VaultBackendPassphrase,
|
||||
Domains: existingConfigFile.Domains,
|
||||
}
|
||||
|
||||
configFileMarshalled, err := json.Marshal(configFile)
|
||||
|
@ -182,8 +182,7 @@ export const useGetImportedSecretsAllEnvs = ({
|
||||
comment: encSecret.secretComment,
|
||||
createdAt: encSecret.createdAt,
|
||||
updatedAt: encSecret.updatedAt,
|
||||
version: encSecret.version,
|
||||
sourceEnv: env
|
||||
version: encSecret.version
|
||||
};
|
||||
})
|
||||
})),
|
||||
|
@ -86,5 +86,13 @@ export const useSecretOverview = (secrets: DashboardProjectSecretsOverview["secr
|
||||
[secrets]
|
||||
);
|
||||
|
||||
return { secKeys, getEnvSecretKeyCount };
|
||||
const getSecretByKey = useCallback(
|
||||
(env: string, key: string) => {
|
||||
const sec = secrets?.find((s) => s.env === env && s.key === key);
|
||||
return sec;
|
||||
},
|
||||
[secrets]
|
||||
);
|
||||
|
||||
return { secKeys, getSecretByKey, getEnvSecretKeyCount };
|
||||
};
|
||||
|
@ -258,7 +258,7 @@ export const OrgMembersTable = ({ handlePopUpOpen, setCompleteInviteLinks }: Pro
|
||||
</Th>
|
||||
<Th className="w-1/3">
|
||||
<div className="flex items-center">
|
||||
Email
|
||||
Username
|
||||
<IconButton
|
||||
variant="plain"
|
||||
className={`ml-2 ${orderBy === OrgMembersOrderBy.Email ? "" : "opacity-30"}`}
|
||||
@ -478,7 +478,7 @@ export const OrgMembersTable = ({ handlePopUpOpen, setCompleteInviteLinks }: Pro
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
|
||||
if (currentOrg?.scimEnabled) {
|
||||
if (currentOrg?.scimEnabled && isActive) {
|
||||
createNotification({
|
||||
text: "You cannot manage users from Infisical when org-level auth is enforced for your organization",
|
||||
type: "error"
|
||||
|
@ -94,8 +94,29 @@ export const AddMemberModal = ({ popUp, handlePopUpToggle }: Props) => {
|
||||
});
|
||||
} else {
|
||||
const inviteeEmails = selectedMembers
|
||||
.map((member) => member?.user.username as string)
|
||||
.filter(Boolean);
|
||||
.map((member) => {
|
||||
if (!member) return null;
|
||||
|
||||
if (member.user.email) {
|
||||
return member.user.email;
|
||||
}
|
||||
|
||||
if (member.user.username) {
|
||||
return member.user.username;
|
||||
}
|
||||
|
||||
return null;
|
||||
})
|
||||
.filter(Boolean) as string[];
|
||||
|
||||
if (inviteeEmails.length !== selectedMembers.length) {
|
||||
createNotification({
|
||||
text: "Failed to add users to project. One or more users were invalid.",
|
||||
type: "error"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (inviteeEmails.length || newInvitees.length) {
|
||||
await addMembersToProject({
|
||||
inviteeEmails: [...inviteeEmails, ...newInvitees],
|
||||
|
@ -81,6 +81,7 @@ import { CreateSecretForm } from "./components/CreateSecretForm";
|
||||
import { FolderBreadCrumbs } from "./components/FolderBreadCrumbs";
|
||||
import { SecretOverviewDynamicSecretRow } from "./components/SecretOverviewDynamicSecretRow";
|
||||
import { SecretOverviewFolderRow } from "./components/SecretOverviewFolderRow";
|
||||
import { SecretOverviewImportListView } from "./components/SecretOverviewImportListView";
|
||||
import {
|
||||
SecretNoAccessOverviewTableRow,
|
||||
SecretOverviewTableRow
|
||||
@ -202,16 +203,12 @@ export const OverviewPage = () => {
|
||||
setVisibleEnvs(userAvailableEnvs);
|
||||
}, [userAvailableEnvs]);
|
||||
|
||||
const {
|
||||
secretImports,
|
||||
isImportedSecretPresentInEnv,
|
||||
getImportedSecretByKey,
|
||||
getEnvImportedSecretKeyCount
|
||||
} = useGetImportedSecretsAllEnvs({
|
||||
projectId: workspaceId,
|
||||
path: secretPath,
|
||||
environments: (userAvailableEnvs || []).map(({ slug }) => slug)
|
||||
});
|
||||
const { isImportedSecretPresentInEnv, getImportedSecretByKey, getEnvImportedSecretKeyCount } =
|
||||
useGetImportedSecretsAllEnvs({
|
||||
projectId: workspaceId,
|
||||
path: secretPath,
|
||||
environments: (userAvailableEnvs || []).map(({ slug }) => slug)
|
||||
});
|
||||
|
||||
const { isPending: isOverviewLoading, data: overview } = useGetProjectSecretsOverview(
|
||||
{
|
||||
@ -235,6 +232,7 @@ export const OverviewPage = () => {
|
||||
secrets,
|
||||
folders,
|
||||
dynamicSecrets,
|
||||
imports,
|
||||
totalFolderCount,
|
||||
totalSecretCount,
|
||||
totalDynamicSecretCount,
|
||||
@ -246,20 +244,16 @@ export const OverviewPage = () => {
|
||||
totalUniqueDynamicSecretsInPage
|
||||
} = overview ?? {};
|
||||
|
||||
const secretImportsShaped = secretImports
|
||||
?.flatMap(({ data }) => data)
|
||||
.filter(Boolean)
|
||||
.flatMap((item) => item?.secrets || []);
|
||||
|
||||
const handleIsImportedSecretPresentInEnv = (envSlug: string, secretName: string) => {
|
||||
if (secrets?.some((s) => s.key === secretName && s.env === envSlug)) {
|
||||
return false;
|
||||
}
|
||||
if (secretImportsShaped.some((s) => s.key === secretName && s.sourceEnv === envSlug)) {
|
||||
return true;
|
||||
}
|
||||
return isImportedSecretPresentInEnv(envSlug, secretName);
|
||||
};
|
||||
const importsShaped = imports
|
||||
?.filter((el) => !el.isReserved)
|
||||
?.map(({ importPath, importEnv }) => ({ importPath, importEnv }))
|
||||
.filter(
|
||||
(el, index, self) =>
|
||||
index ===
|
||||
self.findIndex(
|
||||
(item) => item.importPath === el.importPath && item.importEnv.slug === el.importEnv.slug
|
||||
)
|
||||
);
|
||||
|
||||
useResetPageHelper({
|
||||
totalCount,
|
||||
@ -273,18 +267,7 @@ export const OverviewPage = () => {
|
||||
const { dynamicSecretNames, isDynamicSecretPresentInEnv } =
|
||||
useDynamicSecretOverview(dynamicSecrets);
|
||||
|
||||
const { secKeys, getEnvSecretKeyCount } = useSecretOverview(
|
||||
secrets?.concat(secretImportsShaped) || []
|
||||
);
|
||||
|
||||
const getSecretByKey = useCallback(
|
||||
(env: string, key: string) => {
|
||||
const sec = secrets?.find((s) => s.env === env && s.key === key);
|
||||
return sec;
|
||||
},
|
||||
[secrets]
|
||||
);
|
||||
|
||||
const { secKeys, getSecretByKey, getEnvSecretKeyCount } = useSecretOverview(secrets);
|
||||
const { data: tags } = useGetWsTags(
|
||||
permission.can(ProjectPermissionActions.Read, ProjectPermissionSub.Tags) ? workspaceId : ""
|
||||
);
|
||||
@ -1141,13 +1124,24 @@ export const OverviewPage = () => {
|
||||
key={`overview-${dynamicSecretName}-${index + 1}`}
|
||||
/>
|
||||
))}
|
||||
{filter.import &&
|
||||
importsShaped &&
|
||||
importsShaped?.length > 0 &&
|
||||
importsShaped?.map((item, index) => (
|
||||
<SecretOverviewImportListView
|
||||
secretImport={item}
|
||||
environments={visibleEnvs}
|
||||
key={`overview-secret-input-${index + 1}`}
|
||||
allSecretImports={imports}
|
||||
/>
|
||||
))}
|
||||
{secKeys.map((key, index) => (
|
||||
<SecretOverviewTableRow
|
||||
isSelected={Boolean(selectedEntries.secret[key])}
|
||||
onToggleSecretSelect={() => toggleSelectedEntry(EntryType.SECRET, key)}
|
||||
secretPath={secretPath}
|
||||
getImportedSecretByKey={getImportedSecretByKey}
|
||||
isImportedSecretPresentInEnv={handleIsImportedSecretPresentInEnv}
|
||||
isImportedSecretPresentInEnv={isImportedSecretPresentInEnv}
|
||||
onSecretCreate={handleSecretCreate}
|
||||
onSecretDelete={handleSecretDelete}
|
||||
onSecretUpdate={handleSecretUpdate}
|
||||
|
85
frontend/src/pages/secret-manager/OverviewPage/components/SecretOverviewImportListView/SecretOverviewImportListView.tsx
Normal file
85
frontend/src/pages/secret-manager/OverviewPage/components/SecretOverviewImportListView/SecretOverviewImportListView.tsx
Normal file
@ -0,0 +1,85 @@
|
||||
import { faCheck, faFileImport, faXmark } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
import { Td, Tr } from "@app/components/v2";
|
||||
import { TSecretImport, WorkspaceEnv } from "@app/hooks/api/types";
|
||||
import { EnvFolderIcon } from "@app/pages/secret-manager/SecretDashboardPage/components/SecretImportListView/SecretImportItem";
|
||||
|
||||
type Props = {
|
||||
secretImport: { importPath: string; importEnv: WorkspaceEnv };
|
||||
environments: { name: string; slug: string }[];
|
||||
allSecretImports?: TSecretImport[];
|
||||
};
|
||||
|
||||
export const SecretOverviewImportListView = ({
|
||||
secretImport,
|
||||
environments = [],
|
||||
allSecretImports = []
|
||||
}: Props) => {
|
||||
const isSecretPresentInEnv = (envSlug: string) => {
|
||||
return allSecretImports.some((item) => {
|
||||
if (item.isReplication) {
|
||||
if (
|
||||
item.importPath === secretImport.importPath &&
|
||||
item.importEnv.slug === secretImport.importEnv.slug
|
||||
) {
|
||||
const reservedItem = allSecretImports.find((element) =>
|
||||
element.importPath.includes(`__reserve_replication_${item.id}`)
|
||||
);
|
||||
// If the reserved item exists, check if the envSlug matches
|
||||
if (reservedItem) {
|
||||
return reservedItem.environment === envSlug;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If the item is not replication, check if the envSlug matches directly
|
||||
return (
|
||||
item.environment === envSlug &&
|
||||
item.importPath === secretImport.importPath &&
|
||||
item.importEnv.slug === secretImport.importEnv.slug
|
||||
);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Tr className="group">
|
||||
<Td className="sticky left-0 z-10 border-r border-mineshaft-600 bg-mineshaft-800 bg-clip-padding px-0 py-0 group-hover:bg-mineshaft-700">
|
||||
<div className="group flex cursor-pointer">
|
||||
<div className="flex w-11 items-center py-2 pl-5 text-green-700">
|
||||
<FontAwesomeIcon icon={faFileImport} />
|
||||
</div>
|
||||
<div className="flex flex-grow items-center py-2 pl-4 pr-2">
|
||||
<EnvFolderIcon
|
||||
env={secretImport.importEnv.slug || ""}
|
||||
secretPath={secretImport.importPath || ""}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Td>
|
||||
{environments.map(({ slug }, i) => {
|
||||
const isPresent = isSecretPresentInEnv(slug);
|
||||
return (
|
||||
<Td
|
||||
key={`sec-overview-${slug}-${i + 1}-value`}
|
||||
className={twMerge(
|
||||
"px-0 py-0 group-hover:bg-mineshaft-700",
|
||||
isPresent ? "text-green-600" : "text-red-600"
|
||||
)}
|
||||
>
|
||||
<div className="h-full w-full border-r border-mineshaft-600 px-5 py-[0.85rem]">
|
||||
<div className="flex justify-center">
|
||||
<FontAwesomeIcon
|
||||
// eslint-disable-next-line no-nested-ternary
|
||||
icon={isSecretPresentInEnv(slug) ? faCheck : faXmark}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Td>
|
||||
);
|
||||
})}
|
||||
</Tr>
|
||||
);
|
||||
};
|
1
frontend/src/pages/secret-manager/OverviewPage/components/SecretOverviewImportListView/index.tsx
Normal file
1
frontend/src/pages/secret-manager/OverviewPage/components/SecretOverviewImportListView/index.tsx
Normal file
@ -0,0 +1 @@
|
||||
export { SecretOverviewImportListView } from "./SecretOverviewImportListView";
|
2
frontend/src/pages/secret-manager/OverviewPage/components/SecretOverviewTableRow/SecretRenameRow.tsx
2
frontend/src/pages/secret-manager/OverviewPage/components/SecretOverviewTableRow/SecretRenameRow.tsx
@ -162,7 +162,7 @@ function SecretRenameRow({ environments, getSecretByKey, secretKey, secretPath }
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<Input
|
||||
autoComplete="off"
|
||||
isReadOnly={isReadOnly || secrets.filter(Boolean).length === 0}
|
||||
isReadOnly={isReadOnly}
|
||||
autoCapitalization={currentWorkspace?.autoCapitalization}
|
||||
variant="plain"
|
||||
isDisabled={isOverriden}
|
||||
|
@ -33,7 +33,6 @@ export default defineConfig({
|
||||
// }
|
||||
// }
|
||||
},
|
||||
base: "",
|
||||
plugins: [
|
||||
tsconfigPaths(),
|
||||
nodePolyfills({
|
||||
|
Reference in New Issue
Block a user