Fix merge conflicts

This commit is contained in:
Tuan Dang
2023-08-05 16:57:21 +07:00
29 changed files with 495 additions and 168 deletions
@@ -1,11 +1,11 @@
import { Request, Response } from "express"; import { Request, Response } from "express";
import GitAppInstallationSession from "../../models/gitAppInstallationSession"; import GitAppInstallationSession from "../../ee/models/gitAppInstallationSession";
import crypto from "crypto"; import crypto from "crypto";
import { Types } from "mongoose"; import { Types } from "mongoose";
import { UnauthorizedRequestError } from "../../utils/errors"; import { UnauthorizedRequestError } from "../../utils/errors";
import GitAppOrganizationInstallation from "../../models/gitAppOrganizationInstallation"; import GitAppOrganizationInstallation from "../../ee/models/gitAppOrganizationInstallation";
import { MembershipOrg } from "../../models"; import { MembershipOrg } from "../../models";
import GitRisks, { STATUS_RESOLVED_FALSE_POSITIVE, STATUS_RESOLVED_NOT_REVOKED, STATUS_RESOLVED_REVOKED } from "../../models/gitRisks"; import GitRisks, { STATUS_RESOLVED_FALSE_POSITIVE, STATUS_RESOLVED_NOT_REVOKED, STATUS_RESOLVED_REVOKED } from "../../ee/models/gitRisks";
export const createInstallationSession = async (req: Request, res: Response) => { export const createInstallationSession = async (req: Request, res: Response) => {
const sessionId = crypto.randomBytes(16).toString("hex"); const sessionId = crypto.randomBytes(16).toString("hex");
+3
View File
@@ -6,3 +6,6 @@ export * from "./action";
export * from "./ssoConfig"; export * from "./ssoConfig";
export * from "./trustedIp"; export * from "./trustedIp";
export * from "./auditLog"; export * from "./auditLog";
export * from "./gitRisks";
export * from "./gitAppOrganizationInstallation";
export * from "./gitAppInstallationSession";
+2
View File
@@ -6,6 +6,7 @@ import users from "./users";
import workspace from "./workspace"; import workspace from "./workspace";
import action from "./action"; import action from "./action";
import cloudProducts from "./cloudProducts"; import cloudProducts from "./cloudProducts";
import secretScanning from "./secretScanning";
export { export {
secret, secret,
@@ -16,4 +17,5 @@ export {
workspace, workspace,
action, action,
cloudProducts, cloudProducts,
secretScanning
} }
@@ -4,10 +4,10 @@ import {
requireAuth, requireAuth,
requireOrganizationAuth, requireOrganizationAuth,
validateRequest, validateRequest,
} from "../../middleware"; } from "../../../middleware";
import { body, param } from "express-validator"; import { body, param } from "express-validator";
import { createInstallationSession, getCurrentOrganizationInstallationStatus, getRisksForOrganization, linkInstallationToOrganization, updateRisksStatus } from "../../controllers/v1/secretScanningController"; import { createInstallationSession, getCurrentOrganizationInstallationStatus, getRisksForOrganization, linkInstallationToOrganization, updateRisksStatus } from "../../../controllers/v1/secretScanningController";
import { ACCEPTED, ADMIN, MEMBER, OWNER, AuthMode } from "../../variables"; import { ACCEPTED, ADMIN, AuthMode, MEMBER, OWNER } from "../../../variables";
router.post( router.post(
"/create-installation-session/organization/:organizationId", "/create-installation-session/organization/:organizationId",
@@ -3,13 +3,13 @@ import { exec } from "child_process";
import { mkdir, readFile, rm, writeFile } from "fs"; import { mkdir, readFile, rm, writeFile } from "fs";
import { tmpdir } from "os"; import { tmpdir } from "os";
import { join } from "path" import { join } from "path"
import GitRisks from "../models/gitRisks"; import GitRisks from "../../models/gitRisks";
import GitAppOrganizationInstallation from "../models/gitAppOrganizationInstallation"; import GitAppOrganizationInstallation from "../../models/gitAppOrganizationInstallation";
import MembershipOrg from "../models/membershipOrg"; import MembershipOrg from "../../../models/membershipOrg";
import { ADMIN, OWNER } from "../variables"; import { ADMIN, OWNER } from "../../../variables";
import User from "../models/user"; import User from "../../../models/user";
import { sendMail } from "../helpers"; import { sendMail } from "../../../helpers";
import TelemetryService from "./TelemetryService"; import TelemetryService from "../../../services/TelemetryService";
type SecretMatch = { type SecretMatch = {
Description: string; Description: string;
+3 -1
View File
@@ -2,10 +2,12 @@ import EELicenseService from "./EELicenseService";
import EESecretService from "./EESecretService"; import EESecretService from "./EESecretService";
import EELogService from "./EELogService"; import EELogService from "./EELogService";
import EEAuditLogService from "./EEAuditLogService"; import EEAuditLogService from "./EEAuditLogService";
import GithubSecretScanningService from "./GithubSecretScanning/GithubSecretScanningService"
export { export {
EELicenseService, EELicenseService,
EESecretService, EESecretService,
EELogService, EELogService,
EEAuditLogService EEAuditLogService,
GithubSecretScanningService
} }
+3 -3
View File
@@ -5,8 +5,8 @@ import express from "express";
require("express-async-errors"); require("express-async-errors");
import helmet from "helmet"; import helmet from "helmet";
import cors from "cors"; import cors from "cors";
import { DatabaseService, GithubSecretScanningService } from "./services"; import { DatabaseService } from "./services";
import { EELicenseService } from "./ee/services"; import { EELicenseService, GithubSecretScanningService} from "./ee/services";
import { setUpHealthEndpoint } from "./services/health"; import { setUpHealthEndpoint } from "./services/health";
import cookieParser from "cookie-parser"; import cookieParser from "cookie-parser";
import swaggerUi = require("swagger-ui-express"); import swaggerUi = require("swagger-ui-express");
@@ -24,6 +24,7 @@ import {
secretSnapshot as eeSecretSnapshotRouter, secretSnapshot as eeSecretSnapshotRouter,
users as eeUsersRouter, users as eeUsersRouter,
workspace as eeWorkspaceRouter, workspace as eeWorkspaceRouter,
secretScanning as v1SecretScanningRouter,
} from "./ee/routes/v1"; } from "./ee/routes/v1";
import { import {
auth as v1AuthRouter, auth as v1AuthRouter,
@@ -38,7 +39,6 @@ import {
password as v1PasswordRouter, password as v1PasswordRouter,
secretImport as v1SecretImportRouter, secretImport as v1SecretImportRouter,
secret as v1SecretRouter, secret as v1SecretRouter,
secretScanning as v1SecretScanningRouter,
secretsFolder as v1SecretsFolder, secretsFolder as v1SecretsFolder,
serviceToken as v1ServiceTokenRouter, serviceToken as v1ServiceTokenRouter,
signup as v1SignupRouter, signup as v1SignupRouter,
+1 -4
View File
@@ -24,7 +24,6 @@ import ServiceTokenData, { IServiceTokenData } from "./serviceTokenData";
import APIKeyData, { IAPIKeyData } from "./apiKeyData"; import APIKeyData, { IAPIKeyData } from "./apiKeyData";
import LoginSRPDetail, { ILoginSRPDetail } from "./loginSRPDetail"; import LoginSRPDetail, { ILoginSRPDetail } from "./loginSRPDetail";
import TokenVersion, { ITokenVersion } from "./tokenVersion"; import TokenVersion, { ITokenVersion } from "./tokenVersion";
import GitRisks, { STATUS_RESOLVED_FALSE_POSITIVE } from "./gitRisks";
export { export {
AuthProvider, AuthProvider,
@@ -79,7 +78,5 @@ export {
LoginSRPDetail, LoginSRPDetail,
ILoginSRPDetail, ILoginSRPDetail,
TokenVersion, TokenVersion,
ITokenVersion, ITokenVersion
GitRisks,
STATUS_RESOLVED_FALSE_POSITIVE
}; };
-2
View File
@@ -15,7 +15,6 @@ import password from "./password";
import integration from "./integration"; import integration from "./integration";
import integrationAuth from "./integrationAuth"; import integrationAuth from "./integrationAuth";
import secretsFolder from "./secretsFolder"; import secretsFolder from "./secretsFolder";
import secretScanning from "./secretScanning";
import webhooks from "./webhook"; import webhooks from "./webhook";
import secretImport from "./secretImport"; import secretImport from "./secretImport";
@@ -37,7 +36,6 @@ export {
integration, integration,
integrationAuth, integrationAuth,
secretsFolder, secretsFolder,
secretScanning,
webhooks, webhooks,
secretImport secretImport
}; };
+10 -10
View File
@@ -5,8 +5,8 @@ import { body, param, query } from "express-validator";
import { secretsController } from "../../controllers/v3"; import { secretsController } from "../../controllers/v3";
import { import {
ADMIN, ADMIN,
MEMBER,
AuthMode, AuthMode,
MEMBER,
PERMISSION_READ_SECRETS, PERMISSION_READ_SECRETS,
PERMISSION_WRITE_SECRETS, PERMISSION_WRITE_SECRETS,
SECRET_PERSONAL, SECRET_PERSONAL,
@@ -52,7 +52,7 @@ router.get(
requiredPermissions: [PERMISSION_READ_SECRETS], requiredPermissions: [PERMISSION_READ_SECRETS],
requireBlindIndicesEnabled: true, requireBlindIndicesEnabled: true,
requireE2EEOff: true, requireE2EEOff: true,
checkIPAllowlist: true checkIPAllowlist: false
}), }),
secretsController.getSecretByNameRaw secretsController.getSecretByNameRaw
); );
@@ -80,7 +80,7 @@ router.post(
requiredPermissions: [PERMISSION_WRITE_SECRETS], requiredPermissions: [PERMISSION_WRITE_SECRETS],
requireBlindIndicesEnabled: true, requireBlindIndicesEnabled: true,
requireE2EEOff: true, requireE2EEOff: true,
checkIPAllowlist: true checkIPAllowlist: false
}), }),
secretsController.createSecretRaw secretsController.createSecretRaw
); );
@@ -108,7 +108,7 @@ router.patch(
requiredPermissions: [PERMISSION_WRITE_SECRETS], requiredPermissions: [PERMISSION_WRITE_SECRETS],
requireBlindIndicesEnabled: true, requireBlindIndicesEnabled: true,
requireE2EEOff: true, requireE2EEOff: true,
checkIPAllowlist: true checkIPAllowlist: false
}), }),
secretsController.updateSecretByNameRaw secretsController.updateSecretByNameRaw
); );
@@ -135,7 +135,7 @@ router.delete(
requiredPermissions: [PERMISSION_WRITE_SECRETS], requiredPermissions: [PERMISSION_WRITE_SECRETS],
requireBlindIndicesEnabled: true, requireBlindIndicesEnabled: true,
requireE2EEOff: true, requireE2EEOff: true,
checkIPAllowlist: true checkIPAllowlist: false
}), }),
secretsController.deleteSecretByNameRaw secretsController.deleteSecretByNameRaw
); );
@@ -160,7 +160,7 @@ router.get(
requiredPermissions: [PERMISSION_READ_SECRETS], requiredPermissions: [PERMISSION_READ_SECRETS],
requireBlindIndicesEnabled: true, requireBlindIndicesEnabled: true,
requireE2EEOff: false, requireE2EEOff: false,
checkIPAllowlist: true checkIPAllowlist: false
}), }),
secretsController.getSecrets secretsController.getSecrets
); );
@@ -195,7 +195,7 @@ router.post(
requiredPermissions: [PERMISSION_WRITE_SECRETS], requiredPermissions: [PERMISSION_WRITE_SECRETS],
requireBlindIndicesEnabled: true, requireBlindIndicesEnabled: true,
requireE2EEOff: false, requireE2EEOff: false,
checkIPAllowlist: true checkIPAllowlist: false
}), }),
secretsController.createSecret secretsController.createSecret
); );
@@ -221,7 +221,7 @@ router.get(
locationEnvironment: "query", locationEnvironment: "query",
requiredPermissions: [PERMISSION_READ_SECRETS], requiredPermissions: [PERMISSION_READ_SECRETS],
requireBlindIndicesEnabled: true, requireBlindIndicesEnabled: true,
checkIPAllowlist: true checkIPAllowlist: false
}), }),
secretsController.getSecretByName secretsController.getSecretByName
); );
@@ -251,7 +251,7 @@ router.patch(
requiredPermissions: [PERMISSION_WRITE_SECRETS], requiredPermissions: [PERMISSION_WRITE_SECRETS],
requireBlindIndicesEnabled: true, requireBlindIndicesEnabled: true,
requireE2EEOff: false, requireE2EEOff: false,
checkIPAllowlist: true checkIPAllowlist: false
}), }),
secretsController.updateSecretByName secretsController.updateSecretByName
); );
@@ -278,7 +278,7 @@ router.delete(
requiredPermissions: [PERMISSION_WRITE_SECRETS], requiredPermissions: [PERMISSION_WRITE_SECRETS],
requireBlindIndicesEnabled: true, requireBlindIndicesEnabled: true,
requireE2EEOff: false, requireE2EEOff: false,
checkIPAllowlist: true checkIPAllowlist: false
}), }),
secretsController.deleteSecretByName secretsController.deleteSecretByName
); );
-2
View File
@@ -7,7 +7,6 @@ import EventService from "./EventService";
import IntegrationService from "./IntegrationService"; import IntegrationService from "./IntegrationService";
import TokenService from "./TokenService"; import TokenService from "./TokenService";
import SecretService from "./SecretService"; import SecretService from "./SecretService";
import GithubSecretScanningService from "./GithubSecretScanningService"
export { export {
TelemetryService, TelemetryService,
@@ -18,5 +17,4 @@ export {
IntegrationService, IntegrationService,
TokenService, TokenService,
SecretService, SecretService,
GithubSecretScanningService
} }
+2
View File
@@ -144,12 +144,14 @@ var runCmd = &cobra.Command{
err = executeMultipleCommandWithEnvs(command, len(secretsByKey), env) err = executeMultipleCommandWithEnvs(command, len(secretsByKey), env)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
os.Exit(1)
} }
} else { } else {
err = executeSingleCommandWithEnvs(args, len(secretsByKey), env) err = executeSingleCommandWithEnvs(args, len(secretsByKey), env)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
os.Exit(1)
} }
} }
}, },
+5
View File
@@ -111,3 +111,8 @@ You can use it across various environments, whether it's local development, CI/C
</Tip> </Tip>
</Tab> </Tab>
</Tabs> </Tabs>
## Quick Usage Guide
<Card color="#00A300" href="./usage">
Now that you have the CLI installed on your system, follow this guide to make the best use of it
</Card>
+7 -7
View File
@@ -7,7 +7,7 @@ The CLI is designed for a variety of applications, ranging from local secret man
The distinguishing factor, however, is the authentication method used. The distinguishing factor, however, is the authentication method used.
<Tabs> <Tabs>
<Tab title="Local development"> <Tab title="Local development only">
To use the Infisical CLI in your local development environment, simply run the command below and follow the interactive guide. To use the Infisical CLI in your local development environment, simply run the command below and follow the interactive guide.
```bash ```bash
@@ -32,7 +32,7 @@ The distinguishing factor, however, is the authentication method used.
</Tab> </Tab>
<Tab title="CI/CD, Production usage, etc"> <Tab title="Staging, production & all other use case">
To use Infisical for non local development scenarios, please create a [service token](../documentation/platform/token). The service token will allow you to authenticate and interact with Infisical. To use Infisical for non local development scenarios, please create a [service token](../documentation/platform/token). The service token will allow you to authenticate and interact with Infisical.
Once you have created a service token with the required permissions, you'll need to feed the token to the CLI. Once you have created a service token with the required permissions, you'll need to feed the token to the CLI.
@@ -61,16 +61,16 @@ The distinguishing factor, however, is the authentication method used.
<Tabs> <Tabs>
<Tab title="Feed secrets to your application"> <Tab title="Feed secrets to your application">
```bash ```bash
infisical run -- [your application start command] infisical run --env=dev --path=/apps/firefly -- [your application start command]
# example with node (nodemon) # example with node (nodemon)
infisical run --env=dev --path=/apps/firefly -- nodemon index.js infisical run --env=staging --path=/apps/spotify -- nodemon index.js
# example with flask # example with flask
infisical run -- flask run infisical run --env=prod --path=/apps/backend -- flask run
# example with spring boot - maven # example with spring boot - maven
infisical run -- ./mvnw spring-boot:run --quiet infisical run --env=dev --path=/apps/ -- ./mvnw spring-boot:run --quiet
``` ```
</Tab> </Tab>
<Tab title="Feed secrets via custom aliases (advanced)"> <Tab title="Feed secrets via custom aliases (advanced)">
@@ -86,7 +86,7 @@ The distinguishing factor, however, is the authentication method used.
To make the secrets available from Infisical to `yd`, you can run the following command: To make the secrets available from Infisical to `yd`, you can run the following command:
```bash ```bash
infisical run --command="source custom.sh && yd" infisical run --env=prod --path=/apps/reddit --command="source custom.sh && yd"
``` ```
</Tab> </Tab>
</Tabs> </Tabs>
@@ -3,6 +3,7 @@ import Link from "next/link";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { faGithub,faGoogle } from "@fortawesome/free-brands-svg-icons"; import { faGithub,faGoogle } from "@fortawesome/free-brands-svg-icons";
import { faEnvelope } from "@fortawesome/free-regular-svg-icons"; import { faEnvelope } from "@fortawesome/free-regular-svg-icons";
import { faLock } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Button } from "../v2"; import { Button } from "../v2";
@@ -26,7 +27,7 @@ export default function InitialSignupStep({
window.close(); window.close();
}} }}
leftIcon={<FontAwesomeIcon icon={faGoogle} className="mr-2" />} leftIcon={<FontAwesomeIcon icon={faGoogle} className="mr-2" />}
className="h-14 w-full mx-0" className="h-12 w-full mx-0"
> >
{t("signup.continue-with-google")} {t("signup.continue-with-google")}
</Button> </Button>
@@ -40,7 +41,7 @@ export default function InitialSignupStep({
window.close(); window.close();
}} }}
leftIcon={<FontAwesomeIcon icon={faGithub} className="mr-2" />} leftIcon={<FontAwesomeIcon icon={faGithub} className="mr-2" />}
className="h-14 w-full mx-0" className="h-12 w-full mx-0"
> >
Continue with GitHub Continue with GitHub
</Button> </Button>
@@ -53,7 +54,7 @@ export default function InitialSignupStep({
setIsSignupWithEmail(true); setIsSignupWithEmail(true);
}} }}
leftIcon={<FontAwesomeIcon icon={faEnvelope} className="mr-2" />} leftIcon={<FontAwesomeIcon icon={faEnvelope} className="mr-2" />}
className="h-14 w-full mx-0" className="h-12 w-full mx-0"
> >
Continue with Email Continue with Email
</Button> </Button>
@@ -63,10 +64,10 @@ export default function InitialSignupStep({
colorSchema="primary" colorSchema="primary"
variant="outline_bg" variant="outline_bg"
onClick={() => router.push("/saml-sso")} onClick={() => router.push("/saml-sso")}
isFullWidth leftIcon={<FontAwesomeIcon icon={faLock} className="mr-2" />}
className="h-14 w-full mx-0" className="h-12 w-full mx-0"
> >
Continue with SAML SSO Continue with SSO
</Button> </Button>
</div> </div>
<div className='lg:w-1/6 w-1/4 min-w-[20rem] px-8 text-center mt-6 text-xs text-bunker-400'> <div className='lg:w-1/6 w-1/4 min-w-[20rem] px-8 text-center mt-6 text-xs text-bunker-400'>
+25 -23
View File
@@ -51,10 +51,10 @@ const buttonVariants = cva(
false: "" false: ""
}, },
size: { size: {
xs: ["text-xs", "py-1", "px-1"], xs: ["text-xs", "py-1", "px-2"],
sm: ["text-sm", "py-2", "px-2"], sm: ["text-sm", "py-2", "px-4"],
md: ["text-md", "py-2", "px-4"], md: ["text-md", "py-2", "px-5"],
lg: ["text-lg", "py-2", "px-8"] lg: ["text-lg", "py-2", "px-6"]
} }
}, },
compoundVariants: [ compoundVariants: [
@@ -186,16 +186,17 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
className="absolute rounded-xl opacity-80" className="absolute rounded-xl opacity-80"
/> />
)} )}
<div {leftIcon && (
className={twMerge( <div
"inline-flex shrink-0 cursor-pointer items-center justify-center transition-all", className={twMerge(
loadingToggleClass, "inline-flex shrink-0 cursor-pointer items-center justify-center transition-all",
leftIcon && "ml-2", loadingToggleClass,
size === "xs" ? "mr-1" : "mr-2" size === "xs" ? "mr-1" : "mr-2"
)} )}
> >
{leftIcon} {leftIcon}
</div> </div>
)}
<span <span
className={twMerge( className={twMerge(
"transition-all", "transition-all",
@@ -205,15 +206,16 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
> >
{children} {children}
</span> </span>
<div {rightIcon && (
className={twMerge( <div
"inline-flex shrink-0 cursor-pointer items-center justify-center transition-all", className={twMerge(
loadingToggleClass, "inline-flex shrink-0 cursor-pointer items-center justify-center transition-all",
size === "xs" ? "ml-1" : "ml-2" loadingToggleClass
)} )}
> >
{rightIcon} {rightIcon}
</div> </div>
)}
</button> </button>
); );
} }
@@ -87,7 +87,6 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
ref ref
): JSX.Element => { ): JSX.Element => {
const handleInput = (event: ChangeEvent<HTMLInputElement>) => { const handleInput = (event: ChangeEvent<HTMLInputElement>) => {
console.log(123, props, autoCapitalization)
if (autoCapitalization) { if (autoCapitalization) {
// eslint-disable-next-line no-param-reassign // eslint-disable-next-line no-param-reassign
event.target.value = event.target.value.toUpperCase(); event.target.value = event.target.value.toUpperCase();
+4 -3
View File
@@ -54,13 +54,14 @@ export const useGetProjectSecrets = ({
env, env,
decryptFileKey, decryptFileKey,
isPaused, isPaused,
folderId folderId,
secretPath
}: GetProjectSecretsDTO) => }: GetProjectSecretsDTO) =>
useQuery({ useQuery({
// wait for all values to be available // wait for all values to be available
enabled: Boolean(decryptFileKey && workspaceId && env) && !isPaused, enabled: Boolean(decryptFileKey && workspaceId && env) && !isPaused,
queryKey: secretKeys.getProjectSecret(workspaceId, env, folderId), queryKey: secretKeys.getProjectSecret(workspaceId, env, folderId || secretPath),
queryFn: () => fetchProjectEncryptedSecrets(workspaceId, env, folderId), queryFn: () => fetchProjectEncryptedSecrets(workspaceId, env, folderId, secretPath),
select: useCallback( select: useCallback(
(data: EncryptedSecret[]) => { (data: EncryptedSecret[]) => {
const PRIVATE_KEY = localStorage.getItem("PRIVATE_KEY") as string; const PRIVATE_KEY = localStorage.getItem("PRIVATE_KEY") as string;
+1
View File
@@ -1,3 +1,4 @@
export { useDebounce } from "./useDebounce";
export { useLeaveConfirm } from "./useLeaveConfirm"; export { useLeaveConfirm } from "./useLeaveConfirm";
export { usePersistentState } from "./usePersistentState"; export { usePersistentState } from "./usePersistentState";
export { usePopUp } from "./usePopUp"; export { usePopUp } from "./usePopUp";
+26
View File
@@ -0,0 +1,26 @@
import { useEffect, useState } from "react";
// Ref: https://usehooks.com/useDebounce/
export const useDebounce = <T extends unknown>(value: T, delay = 500): T => {
// State and setters for debounced value
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(
() => {
// Update debounced value after delay
const handler = setTimeout(() => {
setDebouncedValue(value);
}, delay);
// Cancel the timeout if value changes (also on delay change or unmount)
// This is how we prevent debounced value from updating if value is changed ...
// .. within the delay period. Timeout gets cleared and restarted.
return () => {
clearTimeout(handler);
};
},
[value, delay] // Only re-call effect if value or delay changes
);
return debouncedValue;
};
@@ -740,7 +740,7 @@ export const DashboardPage = () => {
return ( return (
<div className="container mx-auto h-full px-6 text-mineshaft-50 dark:[color-scheme:dark]"> <div className="container mx-auto h-full px-6 text-mineshaft-50 dark:[color-scheme:dark]">
<form autoComplete="off" className="h-full"> <form autoComplete="off" className="h-full flex flex-col">
{/* breadcrumb row */} {/* breadcrumb row */}
<div className="relative right-6 -top-2 mb-2 ml-6"> <div className="relative right-6 -top-2 mb-2 ml-6">
<NavHeader <NavHeader
@@ -924,8 +924,8 @@ export const DashboardPage = () => {
</div> </div>
<div <div
className={`${ className={`${
isEmptyPage ? "flex flex-col items-center justify-center" : "" isEmptyPage ? "flex flex-col flex-grow items-center justify-center" : ""
} no-scrollbar::-webkit-scrollbar mt-3 h-3/4 overflow-x-hidden overflow-y-scroll no-scrollbar`} } no-scrollbar::-webkit-scrollbar mt-3 flex flex-col overflow-x-hidden overflow-y-scroll no-scrollbar`}
ref={secretContainer} ref={secretContainer}
> >
{!isEmptyPage && ( {!isEmptyPage && (
@@ -935,7 +935,7 @@ export const DashboardPage = () => {
collisionDetection={closestCenter} collisionDetection={closestCenter}
modifiers={[restrictToVerticalAxis]} modifiers={[restrictToVerticalAxis]}
> >
<TableContainer className="no-scrollbar::-webkit-scrollbar max-h-[calc(100%-120px)] no-scrollbar"> <TableContainer className="no-scrollbar::-webkit-scrollbar max-h-[calc(100%-120px)] no-scrollbar flex-grow">
<table className="secret-table relative"> <table className="secret-table relative">
<SecretTableHeader sortDir={sortDir} onSort={onSortSecrets} /> <SecretTableHeader sortDir={sortDir} onSort={onSortSecrets} />
<tbody className="max-h-96 overflow-y-auto"> <tbody className="max-h-96 overflow-y-auto">
@@ -1017,9 +1017,12 @@ export const DashboardPage = () => {
</FormProvider> </FormProvider>
<SecretDropzone <SecretDropzone
workspaceId={workspaceId}
isSmaller={!isEmptyPage} isSmaller={!isEmptyPage}
onParsedEnv={handleUploadedEnv} onParsedEnv={handleUploadedEnv}
onAddNewSecret={onAppendSecret} onAddNewSecret={onAppendSecret}
environments={userAvailableEnvs}
decryptFileKey={latestFileKey!}
/> />
</div> </div>
{/* secrets table and drawers, modals */} {/* secrets table and drawers, modals */}
@@ -75,6 +75,13 @@ export type FormData = yup.InferType<typeof schema>;
export type TSecretDetailsOpen = { index: number; id: string }; export type TSecretDetailsOpen = { index: number; id: string };
export type TSecOverwriteOpt = { secrets: Record<string, { comments: string[]; value: string }> }; export type TSecOverwriteOpt = { secrets: Record<string, { comments: string[]; value: string }> };
// to convert multi line into single line ones by quoting them and changing to string \n
const formatMultiValueEnv = (val?: string) => {
if (!val) return "";
if (!val.match("\n")) return val;
return `"${val.replace(/\n/g, "\\n")}"`;
};
export const downloadSecret = ( export const downloadSecret = (
secrets: FormData["secrets"] = [], secrets: FormData["secrets"] = [],
importedSecrets: { key: string; value?: string; comment?: string }[] = [], importedSecrets: { key: string; value?: string; comment?: string }[] = [],
@@ -86,9 +93,11 @@ export const downloadSecret = (
}); });
const finalSecret = [...importedSecrets]; const finalSecret = [...importedSecrets];
secrets.forEach(({ key, value, valueOverride, overrideAction, comment }) => { secrets.forEach(({ key, value, valueOverride, overrideAction, comment }) => {
const finalVal =
overrideAction && overrideAction !== SecretActionType.Deleted ? valueOverride : value;
const newValue = { const newValue = {
key, key,
value: overrideAction && overrideAction !== SecretActionType.Deleted ? valueOverride : value, value: formatMultiValueEnv(finalVal),
comment comment
}; };
// can also be zero thus failing // can also be zero thus failing
@@ -1,26 +1,128 @@
import { ChangeEvent, DragEvent } from "react"; import { ChangeEvent, DragEvent, useEffect, useState } from "react";
import { Controller, useForm } from "react-hook-form";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { faUpload } from "@fortawesome/free-solid-svg-icons"; import { faSquareCheck } from "@fortawesome/free-regular-svg-icons";
import {
faClone,
faKey,
faSearch,
faSquareXmark,
faUpload
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { yupResolver } from "@hookform/resolvers/yup";
import { twMerge } from "tailwind-merge"; import { twMerge } from "tailwind-merge";
import * as yup from "yup";
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider"; import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
// TODO:(akhilmhdh) convert all the util functions like this into a lib folder grouped by functionalityj // TODO:(akhilmhdh) convert all the util functions like this into a lib folder grouped by functionality
import { parseDotEnv } from "@app/components/utilities/parseDotEnv"; import { parseDotEnv } from "@app/components/utilities/parseDotEnv";
import { Button } from "@app/components/v2"; import {
import { useToggle } from "@app/hooks/useToggle"; Button,
Checkbox,
EmptyState,
FormControl,
IconButton,
Input,
Modal,
ModalContent,
ModalTrigger,
Select,
SelectItem,
Skeleton,
Tooltip
} from "@app/components/v2";
import { useDebounce, usePopUp, useToggle } from "@app/hooks";
import { useGetProjectSecrets } from "@app/hooks/api";
import { UserWsKeyPair } from "@app/hooks/api/types";
const formSchema = yup.object({
environment: yup.string().required().label("Environment").trim(),
secretPath: yup
.string()
.required()
.label("Secret Path")
.trim()
.transform((val) =>
typeof val === "string" && val.at(-1) === "/" && val.length > 1 ? val.slice(0, -1) : val
),
secrets: yup.lazy((val) => {
const valSchema: Record<string, yup.StringSchema> = {};
Object.keys(val).forEach((key) => {
valSchema[key] = yup.string().trim();
});
return yup.object(valSchema);
})
});
type TFormSchema = yup.InferType<typeof formSchema>;
const parseJson = (src: ArrayBuffer) => {
const file = src.toString();
const formatedData: Record<string, string> = JSON.parse(file);
const env: Record<string, { value: string; comments: string[] }> = {};
Object.keys(formatedData).forEach((key) => {
if (typeof formatedData[key] === "string") {
env[key] = { value: formatedData[key], comments: [] };
}
});
return env;
};
type Props = { type Props = {
isSmaller: boolean; isSmaller: boolean;
onParsedEnv: (env: Record<string, { value: string; comments: string[] }>) => void; onParsedEnv: (env: Record<string, { value: string; comments: string[] }>) => void;
onAddNewSecret?: () => void; onAddNewSecret?: () => void;
environments?: { name: string; slug: string }[];
workspaceId: string;
decryptFileKey: UserWsKeyPair;
}; };
export const SecretDropzone = ({ isSmaller, onParsedEnv, onAddNewSecret }: Props): JSX.Element => { export const SecretDropzone = ({
isSmaller,
onParsedEnv,
onAddNewSecret,
environments = [],
workspaceId,
decryptFileKey
}: Props): JSX.Element => {
const { t } = useTranslation(); const { t } = useTranslation();
const [isDragActive, setDragActive] = useToggle(); const [isDragActive, setDragActive] = useToggle();
const [isLoading, setIsLoading] = useToggle(); const [isLoading, setIsLoading] = useToggle();
const { createNotification } = useNotificationContext(); const { createNotification } = useNotificationContext();
const { popUp, handlePopUpClose, handlePopUpToggle } = usePopUp(["importSecEnv"] as const);
const [searchFilter, setSearchFilter] = useState("");
const [shouldIncludeValues, setShouldIncludeValues] = useState(true);
const {
handleSubmit,
control,
watch,
register,
reset,
setValue,
formState: { isDirty }
} = useForm<TFormSchema>({
resolver: yupResolver(formSchema),
defaultValues: { secretPath: "/", environment: environments?.[0]?.slug }
});
const secretPath = watch("secretPath");
const selectedEnvSlug = watch("environment");
const debouncedSecretPath = useDebounce(secretPath);
const { data: secrets, isLoading: isSecretsLoading } = useGetProjectSecrets({
workspaceId,
env: selectedEnvSlug,
secretPath: debouncedSecretPath,
isPaused: !(Boolean(workspaceId) && Boolean(selectedEnvSlug) && Boolean(debouncedSecretPath)),
decryptFileKey
});
useEffect(() => {
setValue("secrets", {});
setSearchFilter("");
}, [debouncedSecretPath]);
const handleDrag = (e: DragEvent) => { const handleDrag = (e: DragEvent) => {
e.preventDefault(); e.preventDefault();
@@ -32,7 +134,7 @@ export const SecretDropzone = ({ isSmaller, onParsedEnv, onAddNewSecret }: Props
} }
}; };
const parseFile = (file?: File) => { const parseFile = (file?: File, isJson?: boolean) => {
const reader = new FileReader(); const reader = new FileReader();
if (!file) { if (!file) {
createNotification({ createNotification({
@@ -47,7 +149,9 @@ export const SecretDropzone = ({ isSmaller, onParsedEnv, onAddNewSecret }: Props
reader.onload = (event) => { reader.onload = (event) => {
if (!event?.target?.result) return; if (!event?.target?.result) return;
// parse function's argument looks like to be ArrayBuffer // parse function's argument looks like to be ArrayBuffer
const env = parseDotEnv(event.target.result as ArrayBuffer); const env = isJson
? parseJson(event.target.result as ArrayBuffer)
: parseDotEnv(event.target.result as ArrayBuffer);
setIsLoading.off(); setIsLoading.off();
onParsedEnv(env); onParsedEnv(env);
}; };
@@ -74,7 +178,32 @@ export const SecretDropzone = ({ isSmaller, onParsedEnv, onAddNewSecret }: Props
const handleFileUpload = (e: ChangeEvent<HTMLInputElement>) => { const handleFileUpload = (e: ChangeEvent<HTMLInputElement>) => {
e.preventDefault(); e.preventDefault();
parseFile(e.target?.files?.[0]); parseFile(e.target?.files?.[0], e.target?.files?.[0]?.type === "application/json");
};
const handleFormSubmit = (data: TFormSchema) => {
const secretsToBePulled: Record<string, { value: string; comments: string[] }> = {};
Object.keys(data.secrets || {}).forEach((key) => {
if (data.secrets[key]) {
secretsToBePulled[key] = {
value: (shouldIncludeValues && data.secrets[key]) || "",
comments: [""]
};
}
});
onParsedEnv(secretsToBePulled);
handlePopUpClose("importSecEnv");
reset();
};
const handleSecSelectAll = () => {
if (secrets?.secrets) {
setValue(
"secrets",
secrets?.secrets?.reduce((prev, curr) => ({ ...prev, [curr.key]: curr.value }), {}),
{ shouldDirty: true }
);
}
}; };
return ( return (
@@ -84,9 +213,9 @@ export const SecretDropzone = ({ isSmaller, onParsedEnv, onAddNewSecret }: Props
onDragOver={handleDrag} onDragOver={handleDrag}
onDrop={handleDrop} onDrop={handleDrop}
className={twMerge( className={twMerge(
"relative mx-0.5 mb-4 mt-4 flex w-full max-w-[calc(100vw-292px)] cursor-pointer items-center justify-center space-x-2 rounded-md bg-mineshaft-900 py-8 px-2 text-mineshaft-200 opacity-60 outline-dashed outline-2 outline-chicago-600 duration-200 hover:opacity-100", "relative mx-0.5 mb-4 mt-4 flex cursor-pointer items-center justify-center rounded-md bg-mineshaft-900 py-4 text-sm px-2 text-mineshaft-200 opacity-60 outline-dashed outline-2 outline-chicago-600 duration-200 hover:opacity-100",
isDragActive && "opacity-100", isDragActive && "opacity-100",
!isSmaller && "max-w-3xl flex-col space-y-4 py-20", !isSmaller && "w-full max-w-3xl flex-col space-y-4 py-20",
isLoading && "bg-bunker-800" isLoading && "bg-bunker-800"
)} )}
> >
@@ -95,35 +224,184 @@ export const SecretDropzone = ({ isSmaller, onParsedEnv, onAddNewSecret }: Props
<img src="/images/loading/loading.gif" height={70} width={120} alt="loading animation" /> <img src="/images/loading/loading.gif" height={70} width={120} alt="loading animation" />
</div> </div>
) : ( ) : (
<> <form onSubmit={handleSubmit(handleFormSubmit)}>
<div> <div className="flex items-center justify-cente flex-col space-y-2">
<FontAwesomeIcon icon={faUpload} size={isSmaller ? "2x" : "5x"} /> <div>
</div> <FontAwesomeIcon icon={faUpload} size={isSmaller ? "2x" : "5x"} />
<div> </div>
<p className="">{t(isSmaller ? "common.drop-zone-keys" : "common.drop-zone")}</p> <div>
</div> <p className="">{t(isSmaller ? "common.drop-zone-keys" : "common.drop-zone")}</p>
<input </div>
id="fileSelect" <input
type="file" id="fileSelect"
className="absolute h-full w-full cursor-pointer opacity-0" type="file"
accept=".txt,.env,.yml,.yaml" className="absolute h-full w-full cursor-pointer opacity-0"
onChange={handleFileUpload} accept=".txt,.env,.yml,.yaml,.json"
/> onChange={handleFileUpload}
{!isSmaller && ( />
<> <div
<div className="flex w-full flex-row items-center justify-center py-4"> className={twMerge(
<div className="w-1/5 border-t border-mineshaft-700" /> "flex w-full flex-row items-center justify-center py-4",
<p className="mx-4 text-xs text-mineshaft-400">OR</p> isSmaller && "py-1"
<div className="w-1/5 border-t border-mineshaft-700" /> )}
</div> >
<div> <div className="w-1/5 border-t border-mineshaft-700" />
<p className="mx-4 text-xs text-mineshaft-400">OR</p>
<div className="w-1/5 border-t border-mineshaft-700" />
</div>
<div className="flex items-center justify-center space-x-8">
<Modal
isOpen={popUp.importSecEnv.isOpen}
onOpenChange={(isOpen) => {
handlePopUpToggle("importSecEnv", isOpen);
reset();
setSearchFilter("");
}}
>
<ModalTrigger asChild>
<Button variant="star" size={isSmaller ? "xs" : "sm"}>
Copy Secrets From An Environment
</Button>
</ModalTrigger>
<ModalContent
className="max-w-2xl"
title="Copy Secret From An Environment"
subTitle="Copy/paste secrets from other environments into this context"
>
<form>
<div className="flex items-center space-x-2">
<Controller
control={control}
name="environment"
render={({ field: { value, onChange } }) => (
<FormControl label="Environment" isRequired className="w-1/3">
<Select
value={value}
onValueChange={(val) => onChange(val)}
className="w-full border border-mineshaft-500"
defaultValue={environments?.[0]?.slug}
position="popper"
>
{environments.map((sourceEnvironment) => (
<SelectItem
value={sourceEnvironment.slug}
key={`source-environment-${sourceEnvironment.slug}`}
>
{sourceEnvironment.name}
</SelectItem>
))}
</Select>
</FormControl>
)}
/>
<FormControl label="Secret Path" className="flex-grow" isRequired>
<Input
{...register("secretPath")}
placeholder="Provide a path, default is /"
/>
</FormControl>
</div>
<div className="border-t border-mineshaft-600 pt-4">
<div className="mb-4 flex items-center justify-between">
<div>Secrets</div>
<div className="w-1/2 flex items-center space-x-2">
<Input
placeholder="Search for secret"
value={searchFilter}
size="xs"
leftIcon={<FontAwesomeIcon icon={faSearch} />}
onChange={(evt) => setSearchFilter(evt.target.value)}
/>
<Tooltip content="Select All">
<IconButton
ariaLabel="Select all"
variant="outline_bg"
size="xs"
onClick={handleSecSelectAll}
>
<FontAwesomeIcon icon={faSquareCheck} size="lg" />
</IconButton>
</Tooltip>
<Tooltip content="Unselect All">
<IconButton
ariaLabel="UnSelect all"
variant="outline_bg"
size="xs"
onClick={() => reset()}
>
<FontAwesomeIcon icon={faSquareXmark} size="lg" />
</IconButton>
</Tooltip>
</div>
</div>
{!isSecretsLoading && !secrets?.secrets?.length && (
<EmptyState title="No secrets found" icon={faKey} />
)}
<div className="grid grid-cols-2 gap-4 max-h-64 overflow-auto thin-scrollbar ">
{isSecretsLoading &&
Array.apply(0, Array(2)).map((_x, i) => (
<Skeleton
key={`secret-pull-loading-${i + 1}`}
className="bg-mineshaft-700"
/>
))}
{secrets?.secrets
?.filter(({ key }) =>
key.toLowerCase().includes(searchFilter.toLowerCase())
)
?.map(({ _id, key, value: secVal }) => (
<Controller
key={`pull-secret--${_id}`}
control={control}
name={`secrets.${key}`}
render={({ field: { value, onChange } }) => (
<Checkbox
id={`pull-secret-${_id}`}
isChecked={Boolean(value)}
onCheckedChange={(isChecked) => onChange(isChecked ? secVal : "")}
>
{key}
</Checkbox>
)}
/>
))}
</div>
<div className="mt-6 mb-4">
<Checkbox
id="populate-include-value"
isChecked={shouldIncludeValues}
onCheckedChange={(isChecked) =>
setShouldIncludeValues(isChecked as boolean)
}
>
Include secret values
</Checkbox>
</div>
<div className="flex items-center space-x-2">
<Button
leftIcon={<FontAwesomeIcon icon={faClone} />}
type="submit"
isDisabled={!isDirty}
>
Paste Secrets
</Button>
<Button variant="plain" colorSchema="secondary">
Cancel
</Button>
</div>
</div>
</form>
</ModalContent>
</Modal>
{!isSmaller && (
<Button variant="star" onClick={onAddNewSecret}> <Button variant="star" onClick={onAddNewSecret}>
Add a new secret Add a new secret
</Button> </Button>
</div> )}
</> </div>
)}{" "} </div>
</> </form>
)} )}
</div> </div>
); );
@@ -281,7 +281,6 @@ export const SecretInputRow = memo(
value={editorRef} value={editorRef}
isVisible={!isSecretValueHidden} isVisible={!isSecretValueHidden}
onChange={(val, html) => { onChange={(val, html) => {
console.log(val);
onChange(val); onChange(val);
setEditorRef(html); setEditorRef(html);
}} }}
@@ -3,6 +3,7 @@ import { useTranslation } from "react-i18next";
import Link from "next/link"; import Link from "next/link";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { faGithub,faGoogle } from "@fortawesome/free-brands-svg-icons"; import { faGithub,faGoogle } from "@fortawesome/free-brands-svg-icons";
import { faLock } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import axios from "axios" import axios from "axios"
@@ -116,47 +117,6 @@ export const InitialStep = ({
return ( return (
<form onSubmit={handleLogin} className='flex flex-col mx-auto w-full justify-center items-center'> <form onSubmit={handleLogin} className='flex flex-col mx-auto w-full justify-center items-center'>
<h1 className='text-xl font-medium text-transparent bg-clip-text bg-gradient-to-b from-white to-bunker-200 text-center mb-8' >Login to Infisical</h1> <h1 className='text-xl font-medium text-transparent bg-clip-text bg-gradient-to-b from-white to-bunker-200 text-center mb-8' >Login to Infisical</h1>
<div className='lg:w-1/6 w-1/4 min-w-[20rem] flex flex-row items-center my-4 py-2'>
<div className='w-full border-t border-mineshaft-500' />
</div>
<div className='lg:w-1/6 w-1/4 min-w-[21.2rem] md:min-w-[20.1rem] text-center rounded-md'>
<Input
value={email}
onChange={(e) => setEmail(e.target.value)}
type="email"
placeholder="Enter your email..."
isRequired
autoComplete="username"
className="h-12"
/>
</div>
<div className='lg:w-1/6 w-1/4 min-w-[21.2rem] md:min-w-[20.1rem] text-center rounded-md mt-4'>
<Input
value={password}
onChange={(e) => setPassword(e.target.value)}
type="password"
placeholder="Enter your password..."
isRequired
autoComplete="current-password"
id="current-password"
className="h-12 select:-webkit-autofill:focus"
/>
</div>
<div className='lg:w-1/6 w-1/4 min-w-[21.2rem] md:min-w-[20.1rem] text-center rounded-md mt-4'>
<Button
type="submit"
size="sm"
isFullWidth
className='h-12'
colorSchema="primary"
variant="solid"
isLoading={isLoading}
> Continue with Email </Button>
</div>
{!isLoading && loginError && <Error text={t("login.error-login") ?? ""} />}
<div className='lg:w-1/6 w-1/4 min-w-[20rem] flex flex-row items-center mt-4 py-2'>
<div className='w-full border-t border-mineshaft-500' />
</div>
<div className='lg:w-1/6 w-1/4 min-w-[21.2rem] md:min-w-[20.1rem] text-center rounded-md mt-4'> <div className='lg:w-1/6 w-1/4 min-w-[21.2rem] md:min-w-[20.1rem] text-center rounded-md mt-4'>
<Button <Button
colorSchema="primary" colorSchema="primary"
@@ -168,7 +128,7 @@ export const InitialStep = ({
window.close(); window.close();
}} }}
leftIcon={<FontAwesomeIcon icon={faGoogle} className="mr-2" />} leftIcon={<FontAwesomeIcon icon={faGoogle} className="mr-2" />}
className="h-12 w-full mx-0" className="h-11 w-full mx-0"
> >
{t("login.continue-with-google")} {t("login.continue-with-google")}
</Button> </Button>
@@ -185,7 +145,7 @@ export const InitialStep = ({
window.close(); window.close();
}} }}
leftIcon={<FontAwesomeIcon icon={faGithub} className="mr-2" />} leftIcon={<FontAwesomeIcon icon={faGithub} className="mr-2" />}
className="h-12 w-full mx-0" className="h-11 w-full mx-0"
> >
Continue with GitHub Continue with GitHub
</Button> </Button>
@@ -197,12 +157,52 @@ export const InitialStep = ({
onClick={() => { onClick={() => {
setStep(2); setStep(2);
}} }}
isFullWidth leftIcon={<FontAwesomeIcon icon={faLock} className="mr-2" />}
className="h-12 w-full mx-0" className="h-11 w-full mx-0"
> >
Continue with SAML SSO Continue with SSO
</Button> </Button>
</div> </div>
<div className='lg:w-1/6 w-1/4 min-w-[20rem] flex flex-row items-center my-4 py-2'>
<div className='w-full border-t border-mineshaft-400/60' />
<span className="mx-2 text-mineshaft-200 text-xs">or</span>
<div className='w-full border-t border-mineshaft-400/60' />
</div>
<div className='lg:w-1/6 w-1/4 min-w-[21.2rem] md:min-w-[20.1rem] text-center rounded-md'>
<Input
value={email}
onChange={(e) => setEmail(e.target.value)}
type="email"
placeholder="Enter your email..."
isRequired
autoComplete="username"
className="h-11"
/>
</div>
<div className='lg:w-1/6 w-1/4 min-w-[21.2rem] md:min-w-[20.1rem] text-center rounded-md mt-4'>
<Input
value={password}
onChange={(e) => setPassword(e.target.value)}
type="password"
placeholder="Enter your password..."
isRequired
autoComplete="current-password"
id="current-password"
className="h-11 select:-webkit-autofill:focus"
/>
</div>
<div className='lg:w-1/6 w-1/4 min-w-[21.2rem] md:min-w-[20.1rem] text-center rounded-md mt-5'>
<Button
type="submit"
size="sm"
isFullWidth
className='h-11'
colorSchema="primary"
variant="solid"
isLoading={isLoading}
> Continue with Email </Button>
</div>
{!isLoading && loginError && <Error text={t("login.error-login") ?? ""} />}
{ {
!serverDetails?.inviteOnlySignup ? !serverDetails?.inviteOnlySignup ?
<div className="mt-6 text-bunker-400 text-sm flex flex-row"> <div className="mt-6 text-bunker-400 text-sm flex flex-row">
@@ -3,7 +3,7 @@ import crypto from "crypto";
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { faXmark } from "@fortawesome/free-solid-svg-icons"; import { faInfoCircle, faXmark } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import jsrp from "jsrp"; import jsrp from "jsrp";
import nacl from "tweetnacl"; import nacl from "tweetnacl";
@@ -198,7 +198,7 @@ export const UserInfoSSOStep = ({
localStorage.setItem("orgData.id", orgId); localStorage.setItem("orgData.id", orgId);
localStorage.setItem("projectData.id", project._id); localStorage.setItem("projectData.id", project._id);
setStep(1); setStep(1);
} catch (error) { } catch (error) {
setIsLoading(false); setIsLoading(false);
console.error(error); console.error(error);
@@ -256,7 +256,7 @@ export const UserInfoSSOStep = ({
)} )}
<div className="mt-2 flex lg:w-1/6 w-1/4 min-w-[20rem] max-h-60 w-full flex-col items-center justify-center rounded-lg py-2"> <div className="mt-2 flex lg:w-1/6 w-1/4 min-w-[20rem] max-h-60 w-full flex-col items-center justify-center rounded-lg py-2">
<InputField <InputField
label={t("section.password.password")} label="Infisical Password"
onChangeHandler={(pass: string) => { onChangeHandler={(pass: string) => {
setPassword(pass); setPassword(pass);
checkPassword({ checkPassword({
@@ -272,6 +272,7 @@ export const UserInfoSSOStep = ({
autoComplete="new-password" autoComplete="new-password"
id="new-password" id="new-password"
/> />
<div className="mt-2 w-min min-w-[20rem] max-h-60 flex-col items-center justify-center rounded-md px-1.5 bg-mineshaft-500 text-mineshaft-300 text-xs p-1.5"><FontAwesomeIcon icon={faInfoCircle} className="mr-1.5" />Infisical Password is used as part of the encryption mechanism so that even the authentication provider is not able to access your secrets.</div>
{Object.keys(errors).length > 0 && ( {Object.keys(errors).length > 0 && (
<div className="mt-4 flex w-full flex-col items-start rounded-md bg-white/5 px-2 py-2"> <div className="mt-4 flex w-full flex-col items-start rounded-md bg-white/5 px-2 py-2">
<div className="mb-2 text-sm text-gray-400">{t("section.password.validate-base")}</div> <div className="mb-2 text-sm text-gray-400">{t("section.password.validate-base")}</div>
@@ -307,7 +308,7 @@ export const UserInfoSSOStep = ({
onClick={signupErrorCheck} onClick={signupErrorCheck}
size="sm" size="sm"
isFullWidth isFullWidth
className='h-14' className='h-12'
colorSchema="primary" colorSchema="primary"
variant="outline_bg" variant="outline_bg"
isLoading={isLoading} isLoading={isLoading}