mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
Merge remote-tracking branch 'upstream/main' into feat/39
This commit is contained in:
22
README.md
22
README.md
@@ -48,11 +48,11 @@
|
|||||||
|
|
||||||
And more.
|
And more.
|
||||||
|
|
||||||
## Get started
|
## 🚀 Get started
|
||||||
|
|
||||||
To quickly get started, visit our [get started guide](https://infisical.com/docs/getting-started/introduction).
|
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, <i>not just security teams</i>.
|
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, <i>not just security teams</i>.
|
||||||
|
|
||||||
@@ -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.
|
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).
|
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!)!
|
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)
|
- [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 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)
|
- [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)
|
- [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.
|
- [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.
|
- [ ] 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.
|
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 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:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## 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! :)
|
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
|
|||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
|
||||||
## 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.
|
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.
|
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
|
[//]: contributor-faces
|
||||||
|
|
||||||
@@ -277,4 +277,4 @@ Looking to report a security vulnerability? Please don't post about it in GitHub
|
|||||||
<!-- prettier-ignore-start -->
|
<!-- prettier-ignore-start -->
|
||||||
<!-- markdownlint-disable -->
|
<!-- markdownlint-disable -->
|
||||||
|
|
||||||
<a href="https://github.com/dangtony98"><img src="https://avatars.githubusercontent.com/u/25857006?v=4" width="50" height="50" alt=""/></a> <a href="https://github.com/mv-turtle"><img src="https://avatars.githubusercontent.com/u/78047717?s=96&v=4" width="50" height="50" alt=""/></a> <a href="https://github.com/maidul98"><img src="https://avatars.githubusercontent.com/u/9300960?v=4" width="50" height="50" alt=""/></a> <a href="https://github.com/gangjun06"><img src="https://avatars.githubusercontent.com/u/50910815?v=4" width="50" height="50" alt=""/></a> <a href="https://github.com/reginaldbondoc"><img src="https://avatars.githubusercontent.com/u/7693108?v=4" width="50" height="50" alt=""/></a> <a href="https://github.com/SH5H"><img src="https://avatars.githubusercontent.com/u/25437192?v=4" width="50" height="50" alt=""/></a> <a href="https://github.com/hanywang2"><img src="https://avatars.githubusercontent.com/u/44352119?v=4" width="50" height="50" alt=""/></a> <a href="https://github.com/tobias-mintlify"><img src="https://avatars.githubusercontent.com/u/110702161?v=4" width="50" height="50" alt=""/></a> <a href="https://github.com/0xflotus"><img src="https://avatars.githubusercontent.com/u/26602940?v=4" width="50" height="50" alt=""/></a>
|
<a href="https://github.com/dangtony98"><img src="https://avatars.githubusercontent.com/u/25857006?v=4" width="50" height="50" alt=""/></a> <a href="https://github.com/mv-turtle"><img src="https://avatars.githubusercontent.com/u/78047717?s=96&v=4" width="50" height="50" alt=""/></a> <a href="https://github.com/maidul98"><img src="https://avatars.githubusercontent.com/u/9300960?v=4" width="50" height="50" alt=""/></a> <a href="https://github.com/gangjun06"><img src="https://avatars.githubusercontent.com/u/50910815?v=4" width="50" height="50" alt=""/></a> <a href="https://github.com/reginaldbondoc"><img src="https://avatars.githubusercontent.com/u/7693108?v=4" width="50" height="50" alt=""/></a> <a href="https://github.com/SH5H"><img src="https://avatars.githubusercontent.com/u/25437192?v=4" width="50" height="50" alt=""/></a> <a href="https://github.com/asharonbaltazar"><img src="https://avatars.githubusercontent.com/u/58940073?v=4" width="50" height="50" alt=""/></a> <a href="https://github.com/hanywang2"><img src="https://avatars.githubusercontent.com/u/44352119?v=4" width="50" height="50" alt=""/></a> <a href="https://github.com/tobias-mintlify"><img src="https://avatars.githubusercontent.com/u/110702161?v=4" width="50" height="50" alt=""/></a> <a href="https://github.com/0xflotus"><img src="https://avatars.githubusercontent.com/u/26602940?v=4" width="50" height="50" alt=""/></a>
|
||||||
|
|||||||
@@ -1,19 +1,27 @@
|
|||||||
import React from "react";
|
import React, { useState } from "react";
|
||||||
import { useState } from "react";
|
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import {
|
import { faCircle, faEye, faEyeSlash } from "@fortawesome/free-solid-svg-icons";
|
||||||
faCircle,
|
|
||||||
faCircleExclamation,
|
|
||||||
faE,
|
|
||||||
faEye,
|
|
||||||
faEyeSlash,
|
|
||||||
} from "@fortawesome/free-solid-svg-icons";
|
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
|
||||||
import guidGenerator from "../utilities/randomId";
|
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 [passwordVisible, setPasswordVisible] = useState(false);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
@@ -67,7 +75,7 @@ const InputField = (props) => {
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
onChange={(e) => props.onChangeHandler(e.target.value)}
|
onChange={(e) => props.onChangeHandler(e.target.value)}
|
||||||
type={passwordVisible == false ? props.type : "text"}
|
type={passwordVisible === false ? props.type : "text"}
|
||||||
placeholder={props.placeholder}
|
placeholder={props.placeholder}
|
||||||
value={props.value}
|
value={props.value}
|
||||||
required={props.isRequired}
|
required={props.isRequired}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable no-unexpected-multiline */
|
||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
@@ -23,6 +24,7 @@ import getWorkspaces from "~/pages/api/workspace/getWorkspaces";
|
|||||||
import uploadKeys from "~/pages/api/workspace/uploadKeys";
|
import uploadKeys from "~/pages/api/workspace/uploadKeys";
|
||||||
|
|
||||||
import NavBarDashboard from "../navigation/NavBarDashboard";
|
import NavBarDashboard from "../navigation/NavBarDashboard";
|
||||||
|
import { tempLocalStorage } from "../utilities/checks/tempLocalStorage";
|
||||||
import {
|
import {
|
||||||
decryptAssymmetric,
|
decryptAssymmetric,
|
||||||
encryptAssymmetric,
|
encryptAssymmetric,
|
||||||
@@ -31,13 +33,17 @@ import Button from "./buttons/Button";
|
|||||||
import AddWorkspaceDialog from "./dialog/AddWorkspaceDialog";
|
import AddWorkspaceDialog from "./dialog/AddWorkspaceDialog";
|
||||||
import Listbox from "./Listbox";
|
import Listbox from "./Listbox";
|
||||||
|
|
||||||
export default function Layout({ children }) {
|
interface LayoutProps {
|
||||||
|
children: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Layout({ children }: LayoutProps) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [workspaceList, setWorkspaceList] = useState([]);
|
const [workspaceList, setWorkspaceList] = useState([]);
|
||||||
const [workspaceMapping, setWorkspaceMapping] = useState([{ 1: 2 }]);
|
const [workspaceMapping, setWorkspaceMapping] = useState([{ 1: 2 }]);
|
||||||
const [workspaceSelected, setWorkspaceSelected] = useState("∞");
|
const [workspaceSelected, setWorkspaceSelected] = useState("∞");
|
||||||
let [newWorkspaceName, setNewWorkspaceName] = useState("");
|
const [newWorkspaceName, setNewWorkspaceName] = useState("");
|
||||||
let [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [error, setError] = useState(false);
|
const [error, setError] = useState(false);
|
||||||
|
|
||||||
@@ -47,164 +53,186 @@ export default function Layout({ children }) {
|
|||||||
setIsOpen(false);
|
setIsOpen(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function openModal() {
|
||||||
|
setIsOpen(true);
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: what to do about the fact that 2ids can have the same name
|
// 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.
|
* When a user creates a new workspace, redirect them to the page of the new workspace.
|
||||||
* @param {*} workspaceName
|
* @param {*} workspaceName
|
||||||
*/
|
*/
|
||||||
async function submitModal(workspaceName, addAllUsers) {
|
async function submitModal(workspaceName: string, addAllUsers: boolean) {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
// timeout code.
|
||||||
setTimeout(() => setLoading(false), 1500);
|
setTimeout(() => setLoading(false), 1500);
|
||||||
const workspaces = await getWorkspaces();
|
|
||||||
const currentWorkspaces = workspaces.map((workspace) => workspace.name);
|
try {
|
||||||
if (!currentWorkspaces.includes(workspaceName)) {
|
const workspaces = await getWorkspaces();
|
||||||
const newWorkspace = await createWorkspace({
|
const currentWorkspaces = workspaces.map((workspace) => workspace.name);
|
||||||
workspaceName,
|
if (!currentWorkspaces.includes(workspaceName)) {
|
||||||
organizationId: localStorage.getItem("orgData.id"),
|
const newWorkspace = await createWorkspace({
|
||||||
});
|
workspaceName,
|
||||||
let newWorkspaceId;
|
organizationId: tempLocalStorage("orgData.id"),
|
||||||
try {
|
|
||||||
newWorkspaceId = newWorkspace._id;
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
if (addAllUsers) {
|
|
||||||
let orgUsers = await getOrganizationUsers({
|
|
||||||
orgId: localStorage.getItem("orgData.id"),
|
|
||||||
});
|
});
|
||||||
orgUsers.map(async (user) => {
|
const newWorkspaceId = newWorkspace._id;
|
||||||
if (user.status == "accepted") {
|
|
||||||
let result = await addUserToWorkspace(
|
|
||||||
user.user.email,
|
|
||||||
newWorkspaceId
|
|
||||||
);
|
|
||||||
if (result?.invitee && result?.latestKey) {
|
|
||||||
const PRIVATE_KEY = localStorage.getItem("PRIVATE_KEY");
|
|
||||||
|
|
||||||
// assymmetrically decrypt symmetric key with local private key
|
if (addAllUsers) {
|
||||||
const key = decryptAssymmetric({
|
const orgUsers = await getOrganizationUsers({
|
||||||
ciphertext: result.latestKey.encryptedKey,
|
orgId: tempLocalStorage("orgData.id"),
|
||||||
nonce: result.latestKey.nonce,
|
});
|
||||||
publicKey: result.latestKey.sender.publicKey,
|
orgUsers.map(async (user: any) => {
|
||||||
privateKey: PRIVATE_KEY,
|
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({
|
// assymmetrically decrypt symmetric key with local private key
|
||||||
plaintext: key,
|
const key = decryptAssymmetric({
|
||||||
publicKey: result.invitee.publicKey,
|
ciphertext: result.latestKey.encryptedKey,
|
||||||
privateKey: PRIVATE_KEY,
|
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");
|
} catch (err) {
|
||||||
setIsOpen(false);
|
console.error(err);
|
||||||
setNewWorkspaceName("");
|
setError(true);
|
||||||
} else {
|
|
||||||
setError(t("error_project-already-exists"));
|
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function openModal() {
|
|
||||||
setIsOpen(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
const menuItems = [
|
const menuItems = [
|
||||||
{
|
{
|
||||||
href:
|
href:
|
||||||
"/dashboard/" + workspaceMapping[workspaceSelected] + "?Development",
|
"/dashboard/" +
|
||||||
|
workspaceMapping[workspaceSelected as any] +
|
||||||
|
"?Development",
|
||||||
title: t("nav:menu.secrets"),
|
title: t("nav:menu.secrets"),
|
||||||
emoji: <FontAwesomeIcon icon={faKey} />,
|
emoji: <FontAwesomeIcon icon={faKey} />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
href: "/users/" + workspaceMapping[workspaceSelected],
|
href: "/users/" + workspaceMapping[workspaceSelected as any],
|
||||||
title: t("nav:menu.members"),
|
title: t("nav:menu.members"),
|
||||||
emoji: <FontAwesomeIcon icon={faUser} />,
|
emoji: <FontAwesomeIcon icon={faUser} />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
href: "/integrations/" + workspaceMapping[workspaceSelected],
|
href: "/integrations/" + workspaceMapping[workspaceSelected as any],
|
||||||
title: t("nav:menu.integrations"),
|
title: t("nav:menu.integrations"),
|
||||||
emoji: <FontAwesomeIcon icon={faPlug} />,
|
emoji: <FontAwesomeIcon icon={faPlug} />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
href: "/settings/project/" + workspaceMapping[workspaceSelected],
|
href: "/settings/project/" + workspaceMapping[workspaceSelected as any],
|
||||||
title: t("nav:menu.project-settings"),
|
title: t("nav:menu.project-settings"),
|
||||||
emoji: <FontAwesomeIcon icon={faGear} />,
|
emoji: <FontAwesomeIcon icon={faGear} />,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
useEffect(async () => {
|
useEffect(() => {
|
||||||
// Put a user in a workspace if they're not in one yet
|
// Put a user in a workspace if they're not in one yet
|
||||||
if (
|
const putUserInWorkSpace = async () => {
|
||||||
localStorage.getItem("orgData.id") == null ||
|
if (tempLocalStorage("orgData.id") === "") {
|
||||||
localStorage.getItem("orgData.id") == ""
|
const userOrgs = await getOrganizations();
|
||||||
) {
|
localStorage.setItem("orgData.id", userOrgs[0]._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
|
|
||||||
if (
|
|
||||||
intendedWorkspaceId != "heroku" &&
|
|
||||||
!userWorkspaces
|
|
||||||
.map((workspace) => workspace._id)
|
|
||||||
.includes(intendedWorkspaceId)
|
|
||||||
) {
|
|
||||||
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]
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
const orgUserProjects = await getOrganizationUserProjects({
|
||||||
|
orgId: tempLocalStorage("orgData.id"),
|
||||||
|
});
|
||||||
|
const 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
|
||||||
|
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(() => {
|
useEffect(() => {
|
||||||
try {
|
try {
|
||||||
if (
|
if (
|
||||||
workspaceMapping[workspaceSelected] &&
|
workspaceMapping[Number(workspaceSelected)] &&
|
||||||
workspaceMapping[workspaceSelected] !==
|
`${workspaceMapping[Number(workspaceSelected)]}` !==
|
||||||
router.asPath
|
router.asPath
|
||||||
.split("/")
|
.split("/")
|
||||||
[router.asPath.split("/").length - 1].split("?")[0]
|
[router.asPath.split("/").length - 1].split("?")[0]
|
||||||
) {
|
) {
|
||||||
router.push(
|
router.push(
|
||||||
"/dashboard/" + workspaceMapping[workspaceSelected] + "?Development"
|
"/dashboard/" +
|
||||||
|
workspaceMapping[Number(workspaceSelected)] +
|
||||||
|
"?Development"
|
||||||
);
|
);
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
"projectData.id",
|
"projectData.id",
|
||||||
workspaceMapping[workspaceSelected]
|
`${workspaceMapping[Number(workspaceSelected)]}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -221,18 +249,18 @@ export default function Layout({ children }) {
|
|||||||
<nav className="flex flex-col justify-between items-between h-full">
|
<nav className="flex flex-col justify-between items-between h-full">
|
||||||
{/* <div className="py-6"></div> */}
|
{/* <div className="py-6"></div> */}
|
||||||
<div>
|
<div>
|
||||||
<div className="flex justify-center w-full mt-[4.5rem] mb-6 bg-bunker-600 w-full h-20 flex flex-col items-center px-4">
|
<div className="flex justify-center w-full mt-[4.5rem] mb-6 bg-bunker-600 h-20 flex-col items-center px-4">
|
||||||
<div className="text-gray-400 self-start ml-1 mb-1 text-xs font-semibold tracking-wide">
|
<div className="text-gray-400 self-start ml-1 mb-1 text-xs font-semibold tracking-wide">
|
||||||
{t("nav:menu.project")}
|
{t("nav:menu.project")}
|
||||||
</div>
|
</div>
|
||||||
{workspaceList.length > 0 ? (
|
{workspaceList.length > 0 ? (
|
||||||
<Listbox
|
<Listbox
|
||||||
selected={workspaceSelected}
|
selected={workspaceSelected}
|
||||||
onChange={setWorkspaceSelected}
|
onChange={setWorkspaceSelected as any}
|
||||||
data={workspaceList}
|
data={workspaceList}
|
||||||
buttonAction={openModal}
|
buttonAction={openModal}
|
||||||
text=""
|
text=""
|
||||||
workspaceMapping={workspaceMapping}
|
// workspaceMapping={workspaceMapping as any}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Button
|
<Button
|
||||||
@@ -300,7 +328,9 @@ export default function Layout({ children }) {
|
|||||||
Infisical Guide
|
Infisical Guide
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<Link href={`/home/` + workspaceMapping[workspaceSelected]}>
|
<Link
|
||||||
|
href={`/home/` + workspaceMapping[workspaceSelected as any]}
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
className={`flex p-2.5 text-white text-sm rounded cursor-pointer hover:bg-primary-50/5 mt-max border border-dashed border-bunker-400`}
|
className={`flex p-2.5 text-white text-sm rounded cursor-pointer hover:bg-primary-50/5 mt-max border border-dashed border-bunker-400`}
|
||||||
>
|
>
|
||||||
@@ -326,7 +356,7 @@ export default function Layout({ children }) {
|
|||||||
<main className="flex-1 bg-bunker-800">{children}</main>
|
<main className="flex-1 bg-bunker-800">{children}</main>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="block md:hidden bg-bunker-800 w-screen h-screen flex flex-col justify-center items-center">
|
<div className="md:hidden bg-bunker-800 w-screen h-screen flex flex-col justify-center items-center">
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
icon={faMobile}
|
icon={faMobile}
|
||||||
className="text-gray-300 text-7xl mb-8"
|
className="text-gray-300 text-7xl mb-8"
|
||||||
@@ -8,14 +8,13 @@ import {
|
|||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { Listbox, Transition } from "@headlessui/react";
|
import { Listbox, Transition } from "@headlessui/react";
|
||||||
|
|
||||||
|
interface ListBoxProps {
|
||||||
type ListBoxProps = {
|
selected: string;
|
||||||
selected: string,
|
onChange: () => void;
|
||||||
onChange: () => void,
|
data: string[];
|
||||||
data: string[],
|
text: string;
|
||||||
text: string,
|
buttonAction: () => void;
|
||||||
buttonAction: () => void,
|
isFull?: boolean;
|
||||||
width: string,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -35,14 +34,14 @@ export default function ListBox({
|
|||||||
data,
|
data,
|
||||||
text,
|
text,
|
||||||
buttonAction,
|
buttonAction,
|
||||||
width,
|
isFull,
|
||||||
} : ListBoxProps): JSX.Element {
|
}: ListBoxProps): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<Listbox value={selected} onChange={onChange}>
|
<Listbox value={selected} onChange={onChange}>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<Listbox.Button
|
<Listbox.Button
|
||||||
className={`text-gray-400 relative ${
|
className={`text-gray-400 relative ${
|
||||||
width == "full" ? "w-full" : "w-52"
|
isFull ? "w-full" : "w-52"
|
||||||
} cursor-default rounded-md bg-white/[0.07] hover:bg-white/[0.11] duration-200 py-2.5 pl-3 pr-10 text-left shadow-md focus:outline-none focus-visible:border-indigo-500 focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75 focus-visible:ring-offset-2 focus-visible:ring-offset-orange-300 sm:text-sm`}
|
} cursor-default rounded-md bg-white/[0.07] hover:bg-white/[0.11] duration-200 py-2.5 pl-3 pr-10 text-left shadow-md focus:outline-none focus-visible:border-indigo-500 focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75 focus-visible:ring-offset-2 focus-visible:ring-offset-orange-300 sm:text-sm`}
|
||||||
>
|
>
|
||||||
<div className="flex flex-row">
|
<div className="flex flex-row">
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ type ButtonProps = {
|
|||||||
text: string;
|
text: string;
|
||||||
onButtonPressed: () => void;
|
onButtonPressed: () => void;
|
||||||
loading?: boolean;
|
loading?: boolean;
|
||||||
color: string;
|
color?: string;
|
||||||
size: string;
|
size: string;
|
||||||
icon?: IconProp;
|
icon?: IconProp;
|
||||||
active?: boolean;
|
active?: boolean;
|
||||||
|
|||||||
38
frontend/components/context/Notifications/Notification.tsx
Normal file
38
frontend/components/context/Notifications/Notification.tsx
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import { faXmarkCircle } from "@fortawesome/free-solid-svg-icons";
|
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
import classnames from "classnames";
|
||||||
|
|
||||||
|
import { Notification as NotificationType } from "./NotificationProvider";
|
||||||
|
|
||||||
|
interface NotificationProps {
|
||||||
|
notification: NotificationType;
|
||||||
|
clearNotification: (text?: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Notification = ({
|
||||||
|
notification,
|
||||||
|
clearNotification,
|
||||||
|
}: NotificationProps) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={classnames(
|
||||||
|
"w-full flex items-center justify-between px-4 py-3 rounded pointer-events-auto",
|
||||||
|
{
|
||||||
|
"bg-green-600": notification.type === "success",
|
||||||
|
"bg-red-500": notification.type === "error",
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
role="alert"
|
||||||
|
>
|
||||||
|
<p className="text-white text-sm font-bold">{notification.text}</p>
|
||||||
|
<button
|
||||||
|
className="bg-white/5 rounded-lg p-3"
|
||||||
|
onClick={() => clearNotification(notification.text)}
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon className="text-white" icon={faXmarkCircle} />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Notification;
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
import { createContext, ReactNode, useContext, useState } from "react";
|
||||||
|
|
||||||
|
import Notifications from "./Notifications";
|
||||||
|
|
||||||
|
type NotificationType = "success" | "error";
|
||||||
|
|
||||||
|
export type Notification = {
|
||||||
|
text: string;
|
||||||
|
type: NotificationType;
|
||||||
|
};
|
||||||
|
|
||||||
|
type NotificationContextState = {
|
||||||
|
createNotification: ({ text, type }: Notification) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const NotificationContext = createContext<NotificationContextState>({
|
||||||
|
createNotification: () => console.log("createNotification not set!"),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const useNotificationContext = () => useContext(NotificationContext);
|
||||||
|
|
||||||
|
interface NotificationProviderProps {
|
||||||
|
children: ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
const NotificationProvider = ({ children }: NotificationProviderProps) => {
|
||||||
|
const [notifications, setNotifications] = useState<Notification[]>([]);
|
||||||
|
|
||||||
|
const clearNotification = (text?: string) => {
|
||||||
|
if (text) {
|
||||||
|
return setNotifications((state) =>
|
||||||
|
state.filter((notif) => notif.text !== text)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return setNotifications([]);
|
||||||
|
};
|
||||||
|
|
||||||
|
const createNotification = ({ text, type = "success" }: Notification) => {
|
||||||
|
const doesNotifExist = notifications.some((notif) => notif.text === text);
|
||||||
|
|
||||||
|
if (doesNotifExist) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return setNotifications((state) => [...state, { text, type }]);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<NotificationContext.Provider
|
||||||
|
value={{
|
||||||
|
createNotification,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Notifications
|
||||||
|
notifications={notifications}
|
||||||
|
clearNotification={clearNotification}
|
||||||
|
/>
|
||||||
|
{children}
|
||||||
|
</NotificationContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default NotificationProvider;
|
||||||
28
frontend/components/context/Notifications/Notifications.tsx
Normal file
28
frontend/components/context/Notifications/Notifications.tsx
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import Notification from "./Notification";
|
||||||
|
import { Notification as NotificationType } from "./NotificationProvider";
|
||||||
|
|
||||||
|
interface NoticationsProps {
|
||||||
|
notifications: NotificationType[];
|
||||||
|
clearNotification: (text?: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Notifications = ({
|
||||||
|
notifications,
|
||||||
|
clearNotification,
|
||||||
|
}: NoticationsProps) => {
|
||||||
|
return (
|
||||||
|
<div className="hidden fixed z-50 top-1 w-full inset-x-0 pointer-events-none md:flex justify-center">
|
||||||
|
<div className="flex flex-col gap-y-2 w-96">
|
||||||
|
{notifications.map((notif) => (
|
||||||
|
<Notification
|
||||||
|
key={notif.text}
|
||||||
|
notification={notif}
|
||||||
|
clearNotification={clearNotification}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Notifications;
|
||||||
@@ -3,7 +3,7 @@ import token from "~/pages/api/auth/Token";
|
|||||||
export default class SecurityClient {
|
export default class SecurityClient {
|
||||||
static #token = "";
|
static #token = "";
|
||||||
|
|
||||||
contructor() {}
|
constructor() {}
|
||||||
|
|
||||||
static setToken(token) {
|
static setToken(token) {
|
||||||
this.#token = token;
|
this.#token = token;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import getOrganizationUserProjects from "~/pages/api/organization/GetOrgUserProj
|
|||||||
import { initPostHog } from "../analytics/posthog";
|
import { initPostHog } from "../analytics/posthog";
|
||||||
import pushKeys from "./secrets/pushKeys";
|
import pushKeys from "./secrets/pushKeys";
|
||||||
import { ENV } from "./config";
|
import { ENV } from "./config";
|
||||||
|
import { saveTokenToLocalStorage } from "./saveTokenToLocalStorage";
|
||||||
import SecurityClient from "./SecurityClient";
|
import SecurityClient from "./SecurityClient";
|
||||||
|
|
||||||
const nacl = require("tweetnacl");
|
const nacl = require("tweetnacl");
|
||||||
@@ -32,7 +33,6 @@ const attemptLogin = async (
|
|||||||
isLogin
|
isLogin
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
let userWorkspace, userOrg;
|
|
||||||
client.init(
|
client.init(
|
||||||
{
|
{
|
||||||
username: email,
|
username: email,
|
||||||
@@ -41,66 +41,38 @@ const attemptLogin = async (
|
|||||||
async () => {
|
async () => {
|
||||||
const clientPublicKey = client.getPublicKey();
|
const clientPublicKey = client.getPublicKey();
|
||||||
|
|
||||||
let serverPublicKey, salt;
|
const { serverPublicKey, salt } = await login1(email, clientPublicKey);
|
||||||
try {
|
|
||||||
let res = await login1(email, clientPublicKey);
|
|
||||||
res = await res.json();
|
|
||||||
serverPublicKey = res.serverPublicKey;
|
|
||||||
salt = res.salt;
|
|
||||||
} catch (err) {
|
|
||||||
setErrorLogin(true);
|
|
||||||
console.log("Wrong password", err);
|
|
||||||
}
|
|
||||||
|
|
||||||
let response;
|
|
||||||
try {
|
try {
|
||||||
client.setSalt(salt);
|
client.setSalt(salt);
|
||||||
client.setServerPublicKey(serverPublicKey);
|
client.setServerPublicKey(serverPublicKey);
|
||||||
const clientProof = client.getProof(); // called M1
|
const clientProof = client.getProof(); // called M1
|
||||||
response = await login2(email, clientProof);
|
|
||||||
} catch (err) {
|
|
||||||
setErrorLogin(true);
|
|
||||||
console.log("Password verification failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
// if everything works, go the main dashboard page.
|
// if everything works, go the main dashboard page.
|
||||||
try {
|
const { token, publicKey, encryptedPrivateKey, iv, tag } =
|
||||||
if (response.status == "200") {
|
await login2(email, clientProof);
|
||||||
response = await response.json();
|
SecurityClient.setToken(token);
|
||||||
SecurityClient.setToken(response["token"]);
|
|
||||||
const publicKey = response["publicKey"];
|
|
||||||
const encryptedPrivateKey = response["encryptedPrivateKey"];
|
|
||||||
const iv = response["iv"];
|
|
||||||
const tag = response["tag"];
|
|
||||||
|
|
||||||
const PRIVATE_KEY = Aes256Gcm.decrypt(
|
const privateKey = Aes256Gcm.decrypt(
|
||||||
encryptedPrivateKey,
|
encryptedPrivateKey,
|
||||||
iv,
|
iv,
|
||||||
tag,
|
tag,
|
||||||
password
|
password
|
||||||
.slice(0, 32)
|
.slice(0, 32)
|
||||||
.padStart(
|
.padStart(
|
||||||
32 +
|
32 + (password.slice(0, 32).length - new Blob([password]).size),
|
||||||
(password.slice(0, 32).length - new Blob([password]).size),
|
"0"
|
||||||
"0"
|
)
|
||||||
)
|
);
|
||||||
);
|
|
||||||
|
|
||||||
try {
|
saveTokenToLocalStorage({
|
||||||
localStorage.setItem("publicKey", publicKey);
|
token,
|
||||||
localStorage.setItem("encryptedPrivateKey", encryptedPrivateKey);
|
publicKey,
|
||||||
localStorage.setItem("iv", iv);
|
encryptedPrivateKey,
|
||||||
localStorage.setItem("tag", tag);
|
iv,
|
||||||
localStorage.setItem("PRIVATE_KEY", PRIVATE_KEY);
|
tag,
|
||||||
} catch (err) {
|
privateKey,
|
||||||
setErrorLogin(true);
|
});
|
||||||
console.error(
|
|
||||||
"Unable to send the tokens in local storage:" + err.message
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
setErrorLogin(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
const userOrgs = await getOrganizations();
|
const userOrgs = await getOrganizations();
|
||||||
const userOrgsData = userOrgs.map((org) => org._id);
|
const userOrgsData = userOrgs.map((org) => org._id);
|
||||||
@@ -140,14 +112,8 @@ const attemptLogin = async (
|
|||||||
"mongodb+srv://${DB_USERNAME}:${DB_PASSWORD}@mongodb.net",
|
"mongodb+srv://${DB_USERNAME}:${DB_PASSWORD}@mongodb.net",
|
||||||
"personal",
|
"personal",
|
||||||
],
|
],
|
||||||
DB_USERNAME: [
|
DB_USERNAME: ["user1234", "personal"],
|
||||||
"user1234",
|
DB_PASSWORD: ["ah8jak3hk8dhiu4dw7whxwe1l", "personal"],
|
||||||
"personal",
|
|
||||||
],
|
|
||||||
DB_PASSWORD: [
|
|
||||||
"ah8jak3hk8dhiu4dw7whxwe1l",
|
|
||||||
"personal",
|
|
||||||
],
|
|
||||||
TWILIO_AUTH_TOKEN: [
|
TWILIO_AUTH_TOKEN: [
|
||||||
"hgSIwDAKvz8PJfkj6xkzYqzGmAP3HLuG",
|
"hgSIwDAKvz8PJfkj6xkzYqzGmAP3HLuG",
|
||||||
"shared",
|
"shared",
|
||||||
@@ -156,7 +122,7 @@ const attemptLogin = async (
|
|||||||
STRIPE_SECRET_KEY: ["sk_test_7348oyho4hfq398HIUOH78", "shared"],
|
STRIPE_SECRET_KEY: ["sk_test_7348oyho4hfq398HIUOH78", "shared"],
|
||||||
},
|
},
|
||||||
workspaceId: projectToLogin,
|
workspaceId: projectToLogin,
|
||||||
env: "Development"
|
env: "Development",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -1,18 +1,21 @@
|
|||||||
|
interface PasswordCheckProps {
|
||||||
|
password: string;
|
||||||
|
currentErrorCheck: boolean;
|
||||||
|
setPasswordErrorLength: (value: boolean) => void;
|
||||||
|
setPasswordErrorNumber: (value: boolean) => void;
|
||||||
|
setPasswordErrorLowerCase: (value: boolean) => void;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function checks a user password with respect to some criteria.
|
* This function checks a user password with respect to some criteria.
|
||||||
* @param {*} password
|
|
||||||
* @param {*} setPasswordError
|
|
||||||
* @param {*} setPasswordErrorMessage
|
|
||||||
* @param {*} currentErrorCheck
|
|
||||||
* @returns
|
|
||||||
*/
|
*/
|
||||||
const passwordCheck = (
|
const passwordCheck = ({
|
||||||
password,
|
password,
|
||||||
setPasswordErrorLength,
|
setPasswordErrorLength,
|
||||||
setPasswordErrorNumber,
|
setPasswordErrorNumber,
|
||||||
setPasswordErrorLowerCase,
|
setPasswordErrorLowerCase,
|
||||||
currentErrorCheck
|
currentErrorCheck,
|
||||||
) => {
|
}: PasswordCheckProps) => {
|
||||||
let errorCheck = currentErrorCheck;
|
let errorCheck = currentErrorCheck;
|
||||||
if (!password || password.length < 14) {
|
if (!password || password.length < 14) {
|
||||||
setPasswordErrorLength(true);
|
setPasswordErrorLength(true);
|
||||||
11
frontend/components/utilities/checks/tempLocalStorage.ts
Normal file
11
frontend/components/utilities/checks/tempLocalStorage.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
// this is temporary util function. create error handling logic for localStorage and delete this.
|
||||||
|
export const tempLocalStorage = (key: string) => {
|
||||||
|
const value = localStorage.getItem(key);
|
||||||
|
|
||||||
|
if (value === null || value === "") {
|
||||||
|
console.warn("No value found in localStorage for key");
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
};
|
||||||
29
frontend/components/utilities/saveTokenToLocalStorage.ts
Normal file
29
frontend/components/utilities/saveTokenToLocalStorage.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
interface Props {
|
||||||
|
publicKey: string;
|
||||||
|
encryptedPrivateKey: string;
|
||||||
|
iv: string;
|
||||||
|
tag: string;
|
||||||
|
privateTag: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const saveTokenToLocalStorage = ({
|
||||||
|
publicKey,
|
||||||
|
encryptedPrivateKey,
|
||||||
|
iv,
|
||||||
|
tag,
|
||||||
|
privateTag,
|
||||||
|
}: Props) => {
|
||||||
|
try {
|
||||||
|
localStorage.setItem("publicKey", publicKey);
|
||||||
|
localStorage.setItem("encryptedPrivateKey", encryptedPrivateKey);
|
||||||
|
localStorage.setItem("iv", iv);
|
||||||
|
localStorage.setItem("tag", tag);
|
||||||
|
localStorage.setItem("PRIVATE_KEY", privateTag);
|
||||||
|
} catch (err) {
|
||||||
|
if (err instanceof Error) {
|
||||||
|
throw new Error(
|
||||||
|
"Unable to send the tokens in local storage:" + err.message
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
13
frontend/package-lock.json
generated
13
frontend/package-lock.json
generated
@@ -51,6 +51,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tailwindcss/typography": "^0.5.4",
|
"@tailwindcss/typography": "^0.5.4",
|
||||||
"@types/node": "18.11.9",
|
"@types/node": "18.11.9",
|
||||||
|
"@types/react": "^18.0.26",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.45.0",
|
"@typescript-eslint/eslint-plugin": "^5.45.0",
|
||||||
"@typescript-eslint/parser": "^5.45.0",
|
"@typescript-eslint/parser": "^5.45.0",
|
||||||
"autoprefixer": "^10.4.7",
|
"autoprefixer": "^10.4.7",
|
||||||
@@ -1143,9 +1144,9 @@
|
|||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
"node_modules/@types/react": {
|
"node_modules/@types/react": {
|
||||||
"version": "18.0.15",
|
"version": "18.0.26",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.15.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.26.tgz",
|
||||||
"integrity": "sha512-iz3BtLuIYH1uWdsv6wXYdhozhqj20oD4/Hk2DNXIn1kFsmp9x8d9QB6FnPhfkbhd2PgEONt9Q1x/ebkwjfFLow==",
|
"integrity": "sha512-hCR3PJQsAIXyxhTNSiDFY//LhnMZWpNNr5etoCqx/iUfGc5gXWtQR2Phl908jVR6uPXacojQWTg4qRpkxTuGug==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/prop-types": "*",
|
"@types/prop-types": "*",
|
||||||
"@types/scheduler": "*",
|
"@types/scheduler": "*",
|
||||||
@@ -7978,9 +7979,9 @@
|
|||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
"@types/react": {
|
"@types/react": {
|
||||||
"version": "18.0.15",
|
"version": "18.0.26",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.15.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.26.tgz",
|
||||||
"integrity": "sha512-iz3BtLuIYH1uWdsv6wXYdhozhqj20oD4/Hk2DNXIn1kFsmp9x8d9QB6FnPhfkbhd2PgEONt9Q1x/ebkwjfFLow==",
|
"integrity": "sha512-hCR3PJQsAIXyxhTNSiDFY//LhnMZWpNNr5etoCqx/iUfGc5gXWtQR2Phl908jVR6uPXacojQWTg4qRpkxTuGug==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/prop-types": "*",
|
"@types/prop-types": "*",
|
||||||
"@types/scheduler": "*",
|
"@types/scheduler": "*",
|
||||||
|
|||||||
@@ -54,6 +54,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tailwindcss/typography": "^0.5.4",
|
"@tailwindcss/typography": "^0.5.4",
|
||||||
"@types/node": "18.11.9",
|
"@types/node": "18.11.9",
|
||||||
|
"@types/react": "^18.0.26",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.45.0",
|
"@typescript-eslint/eslint-plugin": "^5.45.0",
|
||||||
"@typescript-eslint/parser": "^5.45.0",
|
"@typescript-eslint/parser": "^5.45.0",
|
||||||
"autoprefixer": "^10.4.7",
|
"autoprefixer": "^10.4.7",
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { config } from "@fortawesome/fontawesome-svg-core";
|
|||||||
|
|
||||||
import { initPostHog } from "~/components/analytics/posthog";
|
import { initPostHog } from "~/components/analytics/posthog";
|
||||||
import Layout from "~/components/basic/layout";
|
import Layout from "~/components/basic/layout";
|
||||||
|
import NotificationProvider from "~/components/context/Notifications/NotificationProvider";
|
||||||
import RouteGuard from "~/components/RouteGuard";
|
import RouteGuard from "~/components/RouteGuard";
|
||||||
import { publicPaths } from "~/const";
|
import { publicPaths } from "~/const";
|
||||||
import { ENV } from "~/utilities/config";
|
import { ENV } from "~/utilities/config";
|
||||||
@@ -46,9 +47,11 @@ const App = ({ Component, pageProps, ...appProps }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<RouteGuard>
|
<RouteGuard>
|
||||||
<Layout>
|
<NotificationProvider>
|
||||||
<Component {...pageProps} />
|
<Layout>
|
||||||
</Layout>
|
<Component {...pageProps} />
|
||||||
|
</Layout>
|
||||||
|
</NotificationProvider>
|
||||||
</RouteGuard>
|
</RouteGuard>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
/**
|
|
||||||
* This is the first step of the login process (pake)
|
|
||||||
* @param {*} email
|
|
||||||
* @param {*} clientPublicKey
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
const login1 = (email, clientPublicKey) => {
|
|
||||||
return fetch("/api/v1/auth/login1", {
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
email: email,
|
|
||||||
clientPublicKey,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export default login1;
|
|
||||||
32
frontend/pages/api/auth/Login1.ts
Normal file
32
frontend/pages/api/auth/Login1.ts
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
interface Login1 {
|
||||||
|
serverPublicKey: string;
|
||||||
|
salt: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the first step of the login process (pake)
|
||||||
|
* @param {*} email
|
||||||
|
* @param {*} clientPublicKey
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
const login1 = async (email: string, clientPublicKey: string) => {
|
||||||
|
const response = await fetch("/api/v1/auth/login1", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
email: email,
|
||||||
|
clientPublicKey,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
// need precise error handling about the status code
|
||||||
|
if (response?.status === 200) {
|
||||||
|
const data = (await response.json()) as unknown as Login1;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error("Wrong password");
|
||||||
|
};
|
||||||
|
|
||||||
|
export default login1;
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
/**
|
|
||||||
* This is the second step of the login process
|
|
||||||
* @param {*} email
|
|
||||||
* @param {*} clientPublicKey
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
const login2 = (email, clientProof) => {
|
|
||||||
return fetch("/api/v1/auth/login2", {
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
email: email,
|
|
||||||
clientProof,
|
|
||||||
}),
|
|
||||||
credentials: "include",
|
|
||||||
}).then((res) => {
|
|
||||||
if (res.status == 200) {
|
|
||||||
console.log("User logged in", res);
|
|
||||||
return res;
|
|
||||||
} else {
|
|
||||||
console.log("Failed to log in");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export default login2;
|
|
||||||
36
frontend/pages/api/auth/Login2.ts
Normal file
36
frontend/pages/api/auth/Login2.ts
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
interface Login2Response {
|
||||||
|
encryptedPrivateKey: string;
|
||||||
|
iv: string;
|
||||||
|
publicKey: string;
|
||||||
|
tag: string;
|
||||||
|
token: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the second step of the login process
|
||||||
|
* @param {*} email
|
||||||
|
* @param {*} clientPublicKey
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
const login2 = async (email: string, clientProof: string) => {
|
||||||
|
const response = await fetch("/api/v1/auth/login2", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
email: email,
|
||||||
|
clientProof,
|
||||||
|
}),
|
||||||
|
credentials: "include",
|
||||||
|
});
|
||||||
|
// need precise error handling about the status code
|
||||||
|
if (response.status == 200) {
|
||||||
|
const data = (await response.json()) as unknown as Login2Response;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error("Password verification failed");
|
||||||
|
};
|
||||||
|
|
||||||
|
export default login2;
|
||||||
@@ -6,7 +6,7 @@ import SecurityClient from "~/utilities/SecurityClient";
|
|||||||
* @param {*} res
|
* @param {*} res
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const getOrganizationUserProjects = (req, res) => {
|
const getOrganizationUserProjects = (req) => {
|
||||||
return SecurityClient.fetchCall(
|
return SecurityClient.fetchCall(
|
||||||
"/api/v1/organization/" + req.orgId + "/my-workspaces",
|
"/api/v1/organization/" + req.orgId + "/my-workspaces",
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,13 @@
|
|||||||
import SecurityClient from "~/utilities/SecurityClient";
|
import SecurityClient from "~/utilities/SecurityClient";
|
||||||
|
|
||||||
|
interface Workspaces {
|
||||||
|
__v: number;
|
||||||
|
_id: string;
|
||||||
|
name: string;
|
||||||
|
organization: string;
|
||||||
|
}
|
||||||
|
[];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This route lets us get the workspaces of a certain user
|
* This route lets us get the workspaces of a certain user
|
||||||
* @returns
|
* @returns
|
||||||
@@ -12,10 +20,11 @@ const getWorkspaces = () => {
|
|||||||
},
|
},
|
||||||
}).then(async (res) => {
|
}).then(async (res) => {
|
||||||
if (res?.status == 200) {
|
if (res?.status == 200) {
|
||||||
return (await res.json()).workspaces;
|
const data = (await res.json()) as unknown as { workspaces: Workspaces };
|
||||||
} else {
|
return data.workspaces;
|
||||||
console.log("Failed to get projects");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
throw new Error("Failed to get projects");
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import { Menu, Transition } from "@headlessui/react";
|
|||||||
import Button from "~/components/basic/buttons/Button";
|
import Button from "~/components/basic/buttons/Button";
|
||||||
import ListBox from "~/components/basic/Listbox";
|
import ListBox from "~/components/basic/Listbox";
|
||||||
import BottonRightPopup from "~/components/basic/popups/BottomRightPopup";
|
import BottonRightPopup from "~/components/basic/popups/BottomRightPopup";
|
||||||
|
import { useNotificationContext } from "~/components/context/Notifications/NotificationProvider";
|
||||||
import DashboardInputField from "~/components/dashboard/DashboardInputField";
|
import DashboardInputField from "~/components/dashboard/DashboardInputField";
|
||||||
import DropZone from "~/components/dashboard/DropZone";
|
import DropZone from "~/components/dashboard/DropZone";
|
||||||
import NavHeader from "~/components/navigation/NavHeader";
|
import NavHeader from "~/components/navigation/NavHeader";
|
||||||
@@ -61,7 +62,7 @@ const KeyPair = ({
|
|||||||
modifyValue,
|
modifyValue,
|
||||||
modifyVisibility,
|
modifyVisibility,
|
||||||
isBlurred,
|
isBlurred,
|
||||||
duplicates
|
duplicates,
|
||||||
}) => {
|
}) => {
|
||||||
const [randomStringLength, setRandomStringLength] = useState(32);
|
const [randomStringLength, setRandomStringLength] = useState(32);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@@ -233,6 +234,8 @@ export default function Dashboard() {
|
|||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const { createNotification } = useNotificationContext();
|
||||||
|
|
||||||
// #TODO: fix save message for changing reroutes
|
// #TODO: fix save message for changing reroutes
|
||||||
// const beforeRouteHandler = (url) => {
|
// const beforeRouteHandler = (url) => {
|
||||||
// const warningText =
|
// const warningText =
|
||||||
@@ -376,46 +379,61 @@ export default function Dashboard() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Checking if any of the secret keys start with a number - if so, don't do anything
|
// Checking if any of the secret keys start with a number - if so, don't do anything
|
||||||
const nameErrors = !Object.keys(obj).map(key => !isNaN(key.charAt(0))).every(v => v === false);
|
const nameErrors = !Object.keys(obj)
|
||||||
const duplicatesExist = data?.map(item => item[2]).filter((item, index) => index !== data?.map(item => item[2]).indexOf(item)).length > 0;
|
.map((key) => !isNaN(key.charAt(0)))
|
||||||
|
.every((v) => v === false);
|
||||||
|
const duplicatesExist =
|
||||||
|
data
|
||||||
|
?.map((item) => item[2])
|
||||||
|
.filter(
|
||||||
|
(item, index) => index !== data?.map((item) => item[2]).indexOf(item)
|
||||||
|
).length > 0;
|
||||||
|
|
||||||
if (nameErrors) {
|
if (nameErrors) {
|
||||||
console.log("Solve all name errors first!");
|
return createNotification({
|
||||||
} else if (duplicatesExist) {
|
text: "Solve all name errors first!",
|
||||||
console.log("Remove the duplicated entries first!");
|
type: "error",
|
||||||
} else {
|
|
||||||
// Once "Save changes is clicked", disable that button
|
|
||||||
setButtonReady(false);
|
|
||||||
pushKeys({obj, workspaceId: router.query.id, env});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check which integrations are active for this project and environment
|
|
||||||
* If there are any, update environment variables for those integrations
|
|
||||||
*/
|
|
||||||
let integrations = await getWorkspaceIntegrations({
|
|
||||||
workspaceId: router.query.id,
|
|
||||||
});
|
|
||||||
integrations.map(async (integration) => {
|
|
||||||
if (
|
|
||||||
envMapping[env] == integration.environment &&
|
|
||||||
integration.isActive == true
|
|
||||||
) {
|
|
||||||
let objIntegration = Object.assign(
|
|
||||||
{},
|
|
||||||
...data.map((row) => ({ [row[2]]: row[3] }))
|
|
||||||
);
|
|
||||||
await pushKeysIntegration({
|
|
||||||
obj: objIntegration,
|
|
||||||
integrationId: integration._id,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// If this user has never saved environment variables before, show them a prompt to read docs
|
if (duplicatesExist) {
|
||||||
if (!hasUserEverPushed) {
|
return createNotification({
|
||||||
setCheckDocsPopUpVisible(true);
|
text: "Your secrets weren't saved; please fix the conflicts first.",
|
||||||
await registerUserAction({ action: "first_time_secrets_pushed" });
|
type: "error",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Once "Save changed is clicked", disable that button
|
||||||
|
setButtonReady(false);
|
||||||
|
pushKeys({ obj, workspaceId: router.query.id, env });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check which integrations are active for this project and environment
|
||||||
|
* If there are any, update environment variables for those integrations
|
||||||
|
*/
|
||||||
|
let integrations = await getWorkspaceIntegrations({
|
||||||
|
workspaceId: router.query.id,
|
||||||
|
});
|
||||||
|
integrations.map(async (integration) => {
|
||||||
|
if (
|
||||||
|
envMapping[env] == integration.environment &&
|
||||||
|
integration.isActive == true
|
||||||
|
) {
|
||||||
|
let objIntegration = Object.assign(
|
||||||
|
{},
|
||||||
|
...data.map((row) => ({ [row[2]]: row[3] }))
|
||||||
|
);
|
||||||
|
await pushKeysIntegration({
|
||||||
|
obj: objIntegration,
|
||||||
|
integrationId: integration._id,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// If this user has never saved environment variables before, show them a prompt to read docs
|
||||||
|
if (!hasUserEverPushed) {
|
||||||
|
setCheckDocsPopUpVisible(true);
|
||||||
|
await registerUserAction({ action: "first_time_secrets_pushed" });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -654,7 +672,13 @@ export default function Dashboard() {
|
|||||||
modifyKey={listenChangeKey}
|
modifyKey={listenChangeKey}
|
||||||
modifyVisibility={listenChangeVisibility}
|
modifyVisibility={listenChangeVisibility}
|
||||||
isBlurred={blurred}
|
isBlurred={blurred}
|
||||||
duplicates={data?.map(item => item[2]).filter((item, index) => index !== data?.map(item => item[2]).indexOf(item))}
|
duplicates={data
|
||||||
|
?.map((item) => item[2])
|
||||||
|
.filter(
|
||||||
|
(item, index) =>
|
||||||
|
index !==
|
||||||
|
data?.map((item) => item[2]).indexOf(item)
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@@ -702,7 +726,13 @@ export default function Dashboard() {
|
|||||||
modifyKey={listenChangeKey}
|
modifyKey={listenChangeKey}
|
||||||
modifyVisibility={listenChangeVisibility}
|
modifyVisibility={listenChangeVisibility}
|
||||||
isBlurred={blurred}
|
isBlurred={blurred}
|
||||||
duplicates={data?.map(item => item[2]).filter((item, index) => index !== data?.map(item => item[2]).indexOf(item))}
|
duplicates={data
|
||||||
|
?.map((item) => item[2])
|
||||||
|
.filter(
|
||||||
|
(item, index) =>
|
||||||
|
index !==
|
||||||
|
data?.map((item) => item[2]).indexOf(item)
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import React, { useEffect, useRef, useState } from "react";
|
import React, { useEffect, useRef, useState } from "react";
|
||||||
|
import ReactCodeInput from "react-code-input";
|
||||||
import dynamic from "next/dynamic";
|
import dynamic from "next/dynamic";
|
||||||
import Head from "next/head";
|
import Head from "next/head";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
@@ -22,7 +23,7 @@ import getWorkspaces from "./api/workspace/getWorkspaces";
|
|||||||
import useTranslation from "next-translate/useTranslation";
|
import useTranslation from "next-translate/useTranslation";
|
||||||
import Trans from "next-translate/Trans";
|
import Trans from "next-translate/Trans";
|
||||||
|
|
||||||
const ReactCodeInput = dynamic(import("react-code-input"));
|
// const ReactCodeInput = dynamic(import("react-code-input"));
|
||||||
const nacl = require("tweetnacl");
|
const nacl = require("tweetnacl");
|
||||||
const jsrp = require("jsrp");
|
const jsrp = require("jsrp");
|
||||||
nacl.util = require("tweetnacl-util");
|
nacl.util = require("tweetnacl-util");
|
||||||
@@ -44,7 +45,7 @@ const props = {
|
|||||||
border: "1px solid gray",
|
border: "1px solid gray",
|
||||||
textAlign: "center",
|
textAlign: "center",
|
||||||
},
|
},
|
||||||
};
|
} as const;
|
||||||
const propsPhone = {
|
const propsPhone = {
|
||||||
inputStyle: {
|
inputStyle: {
|
||||||
fontFamily: "monospace",
|
fontFamily: "monospace",
|
||||||
@@ -60,7 +61,7 @@ const propsPhone = {
|
|||||||
border: "1px solid gray",
|
border: "1px solid gray",
|
||||||
textAlign: "center",
|
textAlign: "center",
|
||||||
},
|
},
|
||||||
};
|
} as const;
|
||||||
|
|
||||||
export default function SignUp() {
|
export default function SignUp() {
|
||||||
const [email, setEmail] = useState("");
|
const [email, setEmail] = useState("");
|
||||||
@@ -89,15 +90,16 @@ export default function SignUp() {
|
|||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
useEffect(async () => {
|
useEffect(() => {
|
||||||
let userWorkspace;
|
const tryAuth = async () => {
|
||||||
try {
|
try {
|
||||||
const userWorkspaces = await getWorkspaces();
|
const userWorkspaces = await getWorkspaces();
|
||||||
userWorkspace = userWorkspaces[0]._id;
|
router.push("/dashboard/" + userWorkspaces[0]._id);
|
||||||
router.push("/dashboard/" + userWorkspace);
|
} catch (error) {
|
||||||
} catch (error) {
|
console.log("Error - Not logged in yet");
|
||||||
console.log("Error - Not logged in yet");
|
}
|
||||||
}
|
};
|
||||||
|
tryAuth();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -112,7 +114,7 @@ export default function SignUp() {
|
|||||||
} else if (step == 2) {
|
} else if (step == 2) {
|
||||||
// Checking if the code matches the email.
|
// Checking if the code matches the email.
|
||||||
const response = await checkEmailVerificationCode(email, code);
|
const response = await checkEmailVerificationCode(email, code);
|
||||||
if (response.status == "200" || code == "111222") {
|
if (response.status === 200 || code == "111222") {
|
||||||
setVerificationToken((await response.json()).token);
|
setVerificationToken((await response.json()).token);
|
||||||
setStep(3);
|
setStep(3);
|
||||||
} else {
|
} else {
|
||||||
@@ -127,7 +129,7 @@ export default function SignUp() {
|
|||||||
* Verifies if the entered email "looks" correct
|
* Verifies if the entered email "looks" correct
|
||||||
*/
|
*/
|
||||||
const emailCheck = () => {
|
const emailCheck = () => {
|
||||||
var emailCheckBool = false;
|
let emailCheckBool = false;
|
||||||
if (!email) {
|
if (!email) {
|
||||||
setEmailError(true);
|
setEmailError(true);
|
||||||
setEmailErrorMessage("Please enter your email.");
|
setEmailErrorMessage("Please enter your email.");
|
||||||
@@ -154,7 +156,7 @@ export default function SignUp() {
|
|||||||
// Verifies if the imformation that the users entered (name, workspace) is there, and if the password matched the criteria.
|
// Verifies if the imformation that the users entered (name, workspace) is there, and if the password matched the criteria.
|
||||||
const signupErrorCheck = async () => {
|
const signupErrorCheck = async () => {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
var errorCheck = false;
|
let errorCheck = false;
|
||||||
if (!firstName) {
|
if (!firstName) {
|
||||||
setFirstNameError(true);
|
setFirstNameError(true);
|
||||||
errorCheck = true;
|
errorCheck = true;
|
||||||
@@ -167,13 +169,13 @@ export default function SignUp() {
|
|||||||
} else {
|
} else {
|
||||||
setLastNameError(false);
|
setLastNameError(false);
|
||||||
}
|
}
|
||||||
errorCheck = passwordCheck(
|
errorCheck = passwordCheck({
|
||||||
password,
|
password,
|
||||||
setPasswordErrorLength,
|
setPasswordErrorLength,
|
||||||
setPasswordErrorNumber,
|
setPasswordErrorNumber,
|
||||||
setPasswordErrorLowerCase,
|
setPasswordErrorLowerCase,
|
||||||
errorCheck
|
currentErrorCheck: errorCheck,
|
||||||
);
|
});
|
||||||
|
|
||||||
if (!errorCheck) {
|
if (!errorCheck) {
|
||||||
// Generate a random pair of a public and a private key
|
// Generate a random pair of a public and a private key
|
||||||
@@ -191,7 +193,8 @@ export default function SignUp() {
|
|||||||
32 + (password.slice(0, 32).length - new Blob([password]).size),
|
32 + (password.slice(0, 32).length - new Blob([password]).size),
|
||||||
"0"
|
"0"
|
||||||
)
|
)
|
||||||
);
|
) as { ciphertext: string; iv: string; tag: string };
|
||||||
|
|
||||||
localStorage.setItem("PRIVATE_KEY", PRIVATE_KEY);
|
localStorage.setItem("PRIVATE_KEY", PRIVATE_KEY);
|
||||||
|
|
||||||
client.init(
|
client.init(
|
||||||
@@ -200,45 +203,47 @@ export default function SignUp() {
|
|||||||
password: password,
|
password: password,
|
||||||
},
|
},
|
||||||
async () => {
|
async () => {
|
||||||
client.createVerifier(async (err, result) => {
|
client.createVerifier(
|
||||||
let response = await completeAccountInformationSignup({
|
async (err: any, result: { salt: string; verifier: string }) => {
|
||||||
email,
|
const response = await completeAccountInformationSignup({
|
||||||
firstName,
|
email,
|
||||||
lastName,
|
firstName,
|
||||||
organizationName: firstName + "'s organization",
|
lastName,
|
||||||
publicKey: PUBLIC_KEY,
|
organizationName: firstName + "'s organization",
|
||||||
ciphertext,
|
publicKey: PUBLIC_KEY,
|
||||||
iv,
|
ciphertext,
|
||||||
tag,
|
iv,
|
||||||
salt: result.salt,
|
tag,
|
||||||
verifier: result.verifier,
|
salt: result.salt,
|
||||||
token: verificationToken,
|
verifier: result.verifier,
|
||||||
});
|
token: verificationToken,
|
||||||
|
});
|
||||||
|
|
||||||
// if everything works, go the main dashboard page.
|
// if everything works, go the main dashboard page.
|
||||||
if (!errorCheck && response.status == "200") {
|
if (response.status === 200) {
|
||||||
response = await response.json();
|
// response = await response.json();
|
||||||
|
|
||||||
localStorage.setItem("publicKey", PUBLIC_KEY);
|
localStorage.setItem("publicKey", PUBLIC_KEY);
|
||||||
localStorage.setItem("encryptedPrivateKey", ciphertext);
|
localStorage.setItem("encryptedPrivateKey", ciphertext);
|
||||||
localStorage.setItem("iv", iv);
|
localStorage.setItem("iv", iv);
|
||||||
localStorage.setItem("tag", tag);
|
localStorage.setItem("tag", tag);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await attemptLogin(
|
await attemptLogin(
|
||||||
email,
|
email,
|
||||||
password,
|
password,
|
||||||
setErrorLogin,
|
setErrorLogin,
|
||||||
router,
|
router,
|
||||||
true,
|
true,
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
incrementStep();
|
incrementStep();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@@ -308,6 +313,8 @@ export default function SignUp() {
|
|||||||
|
|
||||||
<div className="hidden md:block">
|
<div className="hidden md:block">
|
||||||
<ReactCodeInput
|
<ReactCodeInput
|
||||||
|
name=""
|
||||||
|
inputMode="tel"
|
||||||
type="text"
|
type="text"
|
||||||
fields={6}
|
fields={6}
|
||||||
onChange={setCode}
|
onChange={setCode}
|
||||||
@@ -317,6 +324,8 @@ export default function SignUp() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="block md:hidden">
|
<div className="block md:hidden">
|
||||||
<ReactCodeInput
|
<ReactCodeInput
|
||||||
|
name=""
|
||||||
|
inputMode="tel"
|
||||||
type="text"
|
type="text"
|
||||||
fields={6}
|
fields={6}
|
||||||
onChange={setCode}
|
onChange={setCode}
|
||||||
@@ -382,15 +391,15 @@ export default function SignUp() {
|
|||||||
<div className="mt-2 flex flex-col items-center justify-center w-full md:p-2 rounded-lg max-h-60">
|
<div className="mt-2 flex flex-col items-center justify-center w-full md:p-2 rounded-lg max-h-60">
|
||||||
<InputField
|
<InputField
|
||||||
label={t("form-password:password")}
|
label={t("form-password:password")}
|
||||||
onChangeHandler={(password) => {
|
onChangeHandler={(password: string) => {
|
||||||
setPassword(password);
|
setPassword(password);
|
||||||
passwordCheck(
|
passwordCheck({
|
||||||
password,
|
password,
|
||||||
setPasswordErrorLength,
|
setPasswordErrorLength,
|
||||||
setPasswordErrorNumber,
|
setPasswordErrorNumber,
|
||||||
setPasswordErrorLowerCase,
|
setPasswordErrorLowerCase,
|
||||||
false
|
currentErrorCheck: false,
|
||||||
);
|
});
|
||||||
}}
|
}}
|
||||||
type="password"
|
type="password"
|
||||||
value={password}
|
value={password}
|
||||||
@@ -508,7 +517,7 @@ export default function SignUp() {
|
|||||||
setBackupKeyIssued,
|
setBackupKeyIssued,
|
||||||
});
|
});
|
||||||
const userWorkspaces = await getWorkspaces();
|
const userWorkspaces = await getWorkspaces();
|
||||||
let userWorkspace = userWorkspaces[0]._id;
|
const userWorkspace = userWorkspaces[0]._id;
|
||||||
router.push("/home/" + userWorkspace);
|
router.push("/home/" + userWorkspace);
|
||||||
}}
|
}}
|
||||||
size="lg"
|
size="lg"
|
||||||
Reference in New Issue
Block a user