diff --git a/README.md b/README.md index 4215a9fa8d..1b2d43c5f6 100644 --- a/README.md +++ b/README.md @@ -48,11 +48,11 @@ And more. -## Get started +## 🚀 Get started To quickly get started, visit our [get started guide](https://infisical.com/docs/getting-started/introduction). -## What's cool about this? +## 🔥 What's cool about this? Infisical makes secret management simple and end-to-end encrypted by default. We're on a mission to make it more accessible to all developers, not just security teams. @@ -62,20 +62,20 @@ If you care about efficiency and security, then Infisical is right for you. We are currently working hard to make Infisical more extensive. Need any integrations or want a new feature? Feel free to [create an issue](https://github.com/Infisical/infisical/issues) or [contribute](https://infisical.com/docs/contributing/overview) directly to the repository. -## Contributing +## 🌱 Contributing Whether it's big or small, we love contributions ❤️ Check out our guide to see how to [get started](https://infisical.com/docs/contributing/overview). Not sure where to get started? [Book a free, non-pressure pairing sessions with one of our teammates](mailto:tony@infisical.com?subject=Pairing%20session&body=I'd%20like%20to%20do%20a%20pairing%20session!)! -## Community & Support +## 💚 Community & Support - [Slack](https://join.slack.com/t/infisical-users/shared_invite/zt-1kdbk07ro-RtoyEt_9E~fyzGo_xQYP6g) (For live discussion with the community and the Infisical team) - [GitHub Discussions](https://github.com/Infisical/infisical/discussions) (For help with building and deeper conversations about features) - [GitHub Issues](https://github.com/Infisical/infisical-cli/issues) (For any bugs and errors you encounter using Infisical) - [Twitter](https://twitter.com/infisical) (Get news fast) -## Status +## 🐥 Status - [x] Public Alpha: Anyone can sign up over at [infisical.com](https://infisical.com) but go easy on us, there are kinks and we're just getting started. - [ ] Public Beta: Stable enough for most non-enterprise use-cases. @@ -83,13 +83,13 @@ Not sure where to get started? [Book a free, non-pressure pairing sessions with We're currently in Public Alpha. -## Stay Up-to-Date +## 🚨 Stay Up-to-Date Infisical officially launched as v.1.0 on November 21st, 2022. However, a lot of new features are coming very quickly. Watch **releases** of this repository to be notified about future updates: ![infisical-star-github](https://github.com/Infisical/infisical/blob/main/.github/images/star-infisical.gif?raw=true) -## Integrations +## 🔌 Integrations We're currently setting the foundation and building [integrations](https://infisical.com/docs/integrations/overview) so secrets can be synced everywhere. Any help is welcome! :) @@ -261,15 +261,15 @@ We're currently setting the foundation and building [integrations](https://infis -## Open-source vs. paid +## 🏘 Open-source vs. paid This repo is entirely MIT licensed, with the exception of the `ee` directory which will contain premium enterprise features requiring a Infisical license in the future. We're currently focused on developing non-enterprise offerings first that should suit most use-cases. -## Security +## 🛡 Security Looking to report a security vulnerability? Please don't post about it in GitHub issue. Instead, refer to our [SECURITY.md](./SECURITY.md) file. -## Contributors 🦸 +## 🦸 Contributors [//]: contributor-faces @@ -277,4 +277,4 @@ Looking to report a security vulnerability? Please don't post about it in GitHub - + diff --git a/frontend/components/basic/InputField.js b/frontend/components/basic/InputField.tsx similarity index 89% rename from frontend/components/basic/InputField.js rename to frontend/components/basic/InputField.tsx index 8368d97976..02bb8a8cc8 100644 --- a/frontend/components/basic/InputField.js +++ b/frontend/components/basic/InputField.tsx @@ -1,19 +1,27 @@ -import React from "react"; -import { useState } from "react"; +import React, { useState } from "react"; import { useRouter } from "next/router"; -import { - faCircle, - faCircleExclamation, - faE, - faEye, - faEyeSlash, -} from "@fortawesome/free-solid-svg-icons"; +import { faCircle, faEye, faEyeSlash } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import guidGenerator from "../utilities/randomId"; -import Error from "./Error"; -const InputField = (props) => { +interface InputFieldProps { + static?: boolean; + label: string; + type: string; + value: string; + placeholder?: string; + isRequired: boolean; + disabled?: boolean; + error?: boolean; + text?: string; + name?: string; + blurred?: boolean; + errorText?: string; + onChangeHandler: (value: string) => void; +} + +const InputField = (props: InputFieldProps) => { const [passwordVisible, setPasswordVisible] = useState(false); const router = useRouter(); @@ -67,7 +75,7 @@ const InputField = (props) => { > props.onChangeHandler(e.target.value)} - type={passwordVisible == false ? props.type : "text"} + type={passwordVisible === false ? props.type : "text"} placeholder={props.placeholder} value={props.value} required={props.isRequired} diff --git a/frontend/components/basic/layout.js b/frontend/components/basic/Layout.tsx similarity index 61% rename from frontend/components/basic/layout.js rename to frontend/components/basic/Layout.tsx index c348888003..6edb7ede5d 100644 --- a/frontend/components/basic/layout.js +++ b/frontend/components/basic/Layout.tsx @@ -1,3 +1,4 @@ +/* eslint-disable no-unexpected-multiline */ /* eslint-disable react-hooks/exhaustive-deps */ import { useEffect, useState } from "react"; import Link from "next/link"; @@ -23,6 +24,7 @@ import getWorkspaces from "~/pages/api/workspace/getWorkspaces"; import uploadKeys from "~/pages/api/workspace/uploadKeys"; import NavBarDashboard from "../navigation/NavBarDashboard"; +import { tempLocalStorage } from "../utilities/checks/tempLocalStorage"; import { decryptAssymmetric, encryptAssymmetric, @@ -31,13 +33,17 @@ import Button from "./buttons/Button"; import AddWorkspaceDialog from "./dialog/AddWorkspaceDialog"; import Listbox from "./Listbox"; -export default function Layout({ children }) { +interface LayoutProps { + children: React.ReactNode; +} + +export default function Layout({ children }: LayoutProps) { const router = useRouter(); const [workspaceList, setWorkspaceList] = useState([]); const [workspaceMapping, setWorkspaceMapping] = useState([{ 1: 2 }]); const [workspaceSelected, setWorkspaceSelected] = useState("∞"); - let [newWorkspaceName, setNewWorkspaceName] = useState(""); - let [isOpen, setIsOpen] = useState(false); + const [newWorkspaceName, setNewWorkspaceName] = useState(""); + const [isOpen, setIsOpen] = useState(false); const [loading, setLoading] = useState(false); const [error, setError] = useState(false); @@ -47,164 +53,186 @@ export default function Layout({ children }) { setIsOpen(false); } + function openModal() { + setIsOpen(true); + } + // TODO: what to do about the fact that 2ids can have the same name /** * When a user creates a new workspace, redirect them to the page of the new workspace. * @param {*} workspaceName */ - async function submitModal(workspaceName, addAllUsers) { + async function submitModal(workspaceName: string, addAllUsers: boolean) { setLoading(true); + // timeout code. setTimeout(() => setLoading(false), 1500); - const workspaces = await getWorkspaces(); - const currentWorkspaces = workspaces.map((workspace) => workspace.name); - if (!currentWorkspaces.includes(workspaceName)) { - const newWorkspace = await createWorkspace({ - workspaceName, - organizationId: localStorage.getItem("orgData.id"), - }); - let newWorkspaceId; - try { - newWorkspaceId = newWorkspace._id; - } catch (error) { - console.log(error); - } - if (addAllUsers) { - let orgUsers = await getOrganizationUsers({ - orgId: localStorage.getItem("orgData.id"), + + try { + const workspaces = await getWorkspaces(); + const currentWorkspaces = workspaces.map((workspace) => workspace.name); + if (!currentWorkspaces.includes(workspaceName)) { + const newWorkspace = await createWorkspace({ + workspaceName, + organizationId: tempLocalStorage("orgData.id"), }); - orgUsers.map(async (user) => { - if (user.status == "accepted") { - let result = await addUserToWorkspace( - user.user.email, - newWorkspaceId - ); - if (result?.invitee && result?.latestKey) { - const PRIVATE_KEY = localStorage.getItem("PRIVATE_KEY"); + const newWorkspaceId = newWorkspace._id; - // assymmetrically decrypt symmetric key with local private key - const key = decryptAssymmetric({ - ciphertext: result.latestKey.encryptedKey, - nonce: result.latestKey.nonce, - publicKey: result.latestKey.sender.publicKey, - privateKey: PRIVATE_KEY, - }); + if (addAllUsers) { + const orgUsers = await getOrganizationUsers({ + orgId: tempLocalStorage("orgData.id"), + }); + orgUsers.map(async (user: any) => { + if (user.status == "accepted") { + const result = await addUserToWorkspace( + user.user.email, + newWorkspaceId + ); + if (result?.invitee && result?.latestKey) { + const PRIVATE_KEY = tempLocalStorage("PRIVATE_KEY"); - const { ciphertext, nonce } = encryptAssymmetric({ - plaintext: key, - publicKey: result.invitee.publicKey, - privateKey: PRIVATE_KEY, - }); + // assymmetrically decrypt symmetric key with local private key + const key = decryptAssymmetric({ + ciphertext: result.latestKey.encryptedKey, + nonce: result.latestKey.nonce, + publicKey: result.latestKey.sender.publicKey, + privateKey: PRIVATE_KEY, + }); - uploadKeys(newWorkspaceId, result.invitee._id, ciphertext, nonce); + const { ciphertext, nonce } = encryptAssymmetric({ + plaintext: key, + publicKey: result.invitee.publicKey, + privateKey: PRIVATE_KEY, + }) as { ciphertext: string; nonce: string }; + + uploadKeys( + newWorkspaceId, + result.invitee._id, + ciphertext, + nonce + ); + } } - } - }); + }); + } + router.push("/dashboard/" + newWorkspaceId + "?Development"); + setIsOpen(false); + setNewWorkspaceName(""); + } else { + console.error("A project with this name already exists."); + setError(true); + setLoading(false); } - router.push("/dashboard/" + newWorkspaceId + "?Development"); - setIsOpen(false); - setNewWorkspaceName(""); - } else { - setError(t("error_project-already-exists")); + } catch (err) { + console.error(err); + setError(true); setLoading(false); } } - function openModal() { - setIsOpen(true); - } - const menuItems = [ { href: - "/dashboard/" + workspaceMapping[workspaceSelected] + "?Development", + "/dashboard/" + + workspaceMapping[workspaceSelected as any] + + "?Development", title: t("nav:menu.secrets"), emoji: , }, { - href: "/users/" + workspaceMapping[workspaceSelected], + href: "/users/" + workspaceMapping[workspaceSelected as any], title: t("nav:menu.members"), emoji: , }, { - href: "/integrations/" + workspaceMapping[workspaceSelected], + href: "/integrations/" + workspaceMapping[workspaceSelected as any], title: t("nav:menu.integrations"), emoji: , }, { - href: "/settings/project/" + workspaceMapping[workspaceSelected], + href: "/settings/project/" + workspaceMapping[workspaceSelected as any], title: t("nav:menu.project-settings"), emoji: , }, ]; - useEffect(async () => { + useEffect(() => { // Put a user in a workspace if they're not in one yet - if ( - localStorage.getItem("orgData.id") == null || - localStorage.getItem("orgData.id") == "" - ) { - const userOrgs = await getOrganizations(); - localStorage.setItem("orgData.id", userOrgs[0]._id); - } + const putUserInWorkSpace = async () => { + if (tempLocalStorage("orgData.id") === "") { + const userOrgs = await getOrganizations(); + localStorage.setItem("orgData.id", userOrgs[0]._id); + } - let orgUserProjects = await getOrganizationUserProjects({ - orgId: localStorage.getItem("orgData.id"), - }); - let userWorkspaces = orgUserProjects; - if ( - userWorkspaces.length == 0 && - router.asPath != "/noprojects" && - !router.asPath.includes("settings") - ) { - router.push("/noprojects"); - } else if (router.asPath != "/noprojects") { - const intendedWorkspaceId = router.asPath - .split("/") - [router.asPath.split("/").length - 1].split("?")[0]; - // If a user is not a member of a workspace they are trying to access, just push them to one of theirs + const orgUserProjects = await getOrganizationUserProjects({ + orgId: tempLocalStorage("orgData.id"), + }); + const userWorkspaces = orgUserProjects; if ( - intendedWorkspaceId != "heroku" && - !userWorkspaces - .map((workspace) => workspace._id) - .includes(intendedWorkspaceId) + userWorkspaces.length == 0 && + router.asPath != "/noprojects" && + !router.asPath.includes("settings") ) { - router.push("/dashboard/" + userWorkspaces[0]._id + "?Development"); - } else { - setWorkspaceList(userWorkspaces.map((workspace) => workspace.name)); - setWorkspaceMapping( - Object.fromEntries( - userWorkspaces.map((workspace) => [workspace.name, workspace._id]) - ) - ); - setWorkspaceSelected( - Object.fromEntries( - userWorkspaces.map((workspace) => [workspace._id, workspace.name]) - )[ - router.asPath - .split("/") - [router.asPath.split("/").length - 1].split("?")[0] - ] - ); + router.push("/noprojects"); + } else if (router.asPath != "/noprojects") { + const intendedWorkspaceId = router.asPath + .split("/") + [router.asPath.split("/").length - 1].split("?")[0]; + // If a user is not a member of a workspace they are trying to access, just push them to one of theirs + if ( + intendedWorkspaceId != "heroku" && + !userWorkspaces + .map((workspace: { _id: string }) => workspace._id) + .includes(intendedWorkspaceId) + ) { + router.push("/dashboard/" + userWorkspaces[0]._id + "?Development"); + } else { + setWorkspaceList( + userWorkspaces.map((workspace: any) => workspace.name) + ); + setWorkspaceMapping( + Object.fromEntries( + userWorkspaces.map((workspace: any) => [ + workspace.name, + workspace._id, + ]) + ) as any + ); + setWorkspaceSelected( + Object.fromEntries( + userWorkspaces.map((workspace: any) => [ + workspace._id, + workspace.name, + ]) + )[ + router.asPath + .split("/") + [router.asPath.split("/").length - 1].split("?")[0] + ] + ); + } } - } + }; + putUserInWorkSpace(); }, []); useEffect(() => { try { if ( - workspaceMapping[workspaceSelected] && - workspaceMapping[workspaceSelected] !== + workspaceMapping[Number(workspaceSelected)] && + `${workspaceMapping[Number(workspaceSelected)]}` !== router.asPath .split("/") [router.asPath.split("/").length - 1].split("?")[0] ) { router.push( - "/dashboard/" + workspaceMapping[workspaceSelected] + "?Development" + "/dashboard/" + + workspaceMapping[Number(workspaceSelected)] + + "?Development" ); localStorage.setItem( "projectData.id", - workspaceMapping[workspaceSelected] + `${workspaceMapping[Number(workspaceSelected)]}` ); } } catch (error) { @@ -221,18 +249,18 @@ export default function Layout({ children }) {