Merge remote-tracking branch 'origin' into groups

This commit is contained in:
Tuan Dang
2024-03-19 10:25:03 -07:00
21 changed files with 90 additions and 116 deletions

View File

@@ -19,7 +19,7 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
.min(1)
.trim()
.refine(
(val) => Object.keys(OrgMembershipRole).includes(val),
(val) => !Object.keys(OrgMembershipRole).includes(val),
"Please choose a different slug, the slug you have entered is reserved"
)
.refine((v) => slugify(v) === v, {

View File

@@ -9,6 +9,7 @@ import jmespath from "jmespath";
import knex from "knex";
import { getConfig } from "@app/lib/config/env";
import { getDbConnectionHost } from "@app/lib/knex";
import { alphaNumericNanoId } from "@app/lib/nanoid";
import { TAssignOp, TDbProviderClients, TDirectAssignOp, THttpProviderFunction } from "../templates/types";
@@ -89,7 +90,7 @@ export const secretRotationDbFn = async ({
const appCfg = getConfig();
const ssl = ca ? { rejectUnauthorized: false, ca } : undefined;
if (host === "localhost" || host === "127.0.0.1" || appCfg.DB_CONNECTION_URI.includes(host))
if (host === "localhost" || host === "127.0.0.1" || getDbConnectionHost(appCfg.DB_CONNECTION_URI) === host)
throw new Error("Invalid db host");
const db = knex({

View File

@@ -0,0 +1,11 @@
import { URL } from "url"; // Import the URL class
export const getDbConnectionHost = (urlString: string) => {
try {
const url = new URL(urlString);
// Split hostname and port (if provided)
return url.hostname.split(":")[0];
} catch (error) {
return null;
}
};

View File

@@ -4,6 +4,7 @@ import { Tables } from "knex/types/tables";
import { DatabaseError } from "../errors";
export * from "./connection";
export * from "./join";
export * from "./select";

View File

@@ -14,13 +14,13 @@ export const fastifySwagger = fp(async (fastify) => {
version: "0.0.1"
},
servers: [
{
url: "http://localhost:8080",
description: "Local server"
},
{
url: "https://app.infisical.com",
description: "Production server"
},
{
url: "http://localhost:8080",
description: "Local server"
}
],
components: {

View File

@@ -150,8 +150,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
message: "Slug must be a valid slug"
})
.optional()
.describe(PROJECTS.CREATE.slug),
organizationSlug: z.string().trim().describe(PROJECTS.CREATE.organizationSlug)
.describe(PROJECTS.CREATE.slug)
}),
response: {
200: z.object({
@@ -166,7 +165,6 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
actor: req.permission.type,
actorOrgId: req.permission.orgId,
actorAuthMethod: req.permission.authMethod,
orgSlug: req.body.organizationSlug,
workspaceName: req.body.projectName,
slug: req.body.slug
});

View File

@@ -92,7 +92,6 @@ export const projectServiceFactory = ({
* Create workspace. Make user the admin
* */
const createProject = async ({
orgSlug,
actor,
actorId,
actorOrgId,
@@ -100,13 +99,7 @@ export const projectServiceFactory = ({
workspaceName,
slug: projectSlug
}: TCreateProjectDTO) => {
if (!orgSlug) {
throw new BadRequestError({
message: "Must provide organization slug to create project"
});
}
const organization = await orgDAL.findOne({ slug: orgSlug });
const organization = await orgDAL.findOne({ id: actorOrgId });
const { permission, membership: orgMembership } = await permissionService.getOrgPermission(
actor,

View File

@@ -24,7 +24,6 @@ export type TCreateProjectDTO = {
actorAuthMethod: ActorAuthMethod;
actorId: string;
actorOrgId?: string;
orgSlug: string;
workspaceName: string;
slug?: string;
};

View File

@@ -9,7 +9,7 @@ This guide walks through how you can use these paid features in Infisical.
<Steps>
<Step title="Purchase a license">
Start by either signing up for a free demo [here](https://infisical.com/schedule-demo) or contacting team@infisical.com to purchase a license.
Start by either signing up for a free demo [here](https://infisical.com/schedule-demo) or contacting sales@infisical.com to purchase a license.
Once purchased, you will be issued a license key.
</Step>
@@ -25,4 +25,4 @@ This guide walks through how you can use these paid features in Infisical.
Once the license expires, Infisical will continue to run, but EE features will be disabled until the license is renewed or a new one is purchased.
</Note>
</Step>
</Steps>
</Steps>

View File

@@ -196,10 +196,8 @@ export default function UserInfoStep({
const userOrgs = await fetchOrganizations();
const orgSlug = userOrgs[0]?.slug;
const orgId = userOrgs[0]?.id;
const project = await ProjectService.initProject({
organizationSlug: orgSlug,
projectName: "Example Project"
});

View File

@@ -7,6 +7,7 @@
/* eslint-disable vars-on-top */
/* eslint-disable no-var */
/* eslint-disable func-names */
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-nocheck
import { INTERCOMid as APPid } from "@app/components/utilities/config";

View File

@@ -1,3 +1,4 @@
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-nocheck
/* eslint-disable import/no-extraneous-dependencies */
/* eslint-disable global-require */

View File

@@ -77,18 +77,11 @@ const secretsToBeAdded = [
* @param {String} obj.projectName - name of new project
* @returns {Project} project - new project
*/
const initProjectHelper = async ({
organizationSlug,
projectName
}: {
organizationSlug: string;
projectName: string;
}) => {
const initProjectHelper = async ({ projectName }: { projectName: string }) => {
// create new project
const {
data: { project }
} = await createWorkspace({
organizationSlug,
projectName
});

View File

@@ -199,19 +199,17 @@ export const useGetWorkspaceIntegrations = (workspaceId: string) =>
});
export const createWorkspace = ({
organizationSlug,
projectName
}: CreateWorkspaceDTO): Promise<{ data: { project: Workspace } }> => {
return apiRequest.post("/api/v2/workspace", { projectName, organizationSlug });
return apiRequest.post("/api/v2/workspace", { projectName });
};
export const useCreateWorkspace = () => {
const queryClient = useQueryClient();
return useMutation<{ data: { project: Workspace } }, {}, CreateWorkspaceDTO>({
mutationFn: async ({ organizationSlug, projectName }) =>
mutationFn: async ({ projectName }) =>
createWorkspace({
organizationSlug,
projectName
}),
onSuccess: () => {
@@ -325,7 +323,13 @@ export const useDeleteUserFromWorkspace = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async ({ usernames, workspaceId }: { workspaceId: string; usernames: string[] }) => {
mutationFn: async ({
usernames,
workspaceId
}: {
workspaceId: string;
usernames: string[];
}) => {
const {
data: { deletedMembership }
} = await apiRequest.delete(`/api/v2/workspace/${workspaceId}/memberships`, {
@@ -391,11 +395,7 @@ export const useAddIdentityToWorkspace = () => {
export const useUpdateIdentityWorkspaceRole = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async ({
identityId,
workspaceId,
roles
}:TUpdateWorkspaceIdentityRoleDTO)=> {
mutationFn: async ({ identityId, workspaceId, roles }: TUpdateWorkspaceIdentityRoleDTO) => {
const {
data: { identityMembership }
} = await apiRequest.patch(

View File

@@ -45,7 +45,6 @@ export type TGetUpgradeProjectStatusDTO = {
// mutation dto
export type CreateWorkspaceDTO = {
projectName: string;
organizationSlug: string;
};
export type RenameWorkspaceDTO = { workspaceID: string; newWorkspaceName: string };
@@ -82,16 +81,16 @@ export type TUpdateWorkspaceUserRoleDTO = {
workspaceId: string;
roles: (
| {
role: string;
isTemporary?: false;
}
role: string;
isTemporary?: false;
}
| {
role: string;
isTemporary: true;
temporaryMode: ProjectUserMembershipTemporaryMode;
temporaryRange: string;
temporaryAccessStartTime: string;
}
role: string;
isTemporary: true;
temporaryMode: ProjectUserMembershipTemporaryMode;
temporaryRange: string;
temporaryAccessStartTime: string;
}
)[];
};
@@ -100,15 +99,15 @@ export type TUpdateWorkspaceIdentityRoleDTO = {
workspaceId: string;
roles: (
| {
role: string;
isTemporary?: false;
}
role: string;
isTemporary?: false;
}
| {
role: string;
isTemporary: true;
temporaryMode: ProjectUserMembershipTemporaryMode;
temporaryRange: string;
temporaryAccessStartTime: string;
}
role: string;
isTemporary: true;
temporaryMode: ProjectUserMembershipTemporaryMode;
temporaryRange: string;
temporaryAccessStartTime: string;
}
)[];
};

View File

@@ -4,6 +4,7 @@
/* eslint-disable vars-on-top */
/* eslint-disable no-var */
/* eslint-disable func-names */
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-nocheck
import { useTranslation } from "react-i18next";
@@ -91,7 +92,7 @@ export const AdminLayout = ({ children }: LayoutProps) => {
console.error(error);
}
};
return (
<>
<div className="dark hidden h-screen w-full flex-col overflow-x-hidden md:flex">

View File

@@ -236,7 +236,6 @@ export const AppLayout = ({ children }: LayoutProps) => {
project: { id: newProjectId }
}
} = await createWs.mutateAsync({
organizationSlug: currentOrg.slug,
projectName: name
});

View File

@@ -1,5 +1,3 @@
/* eslint-disable jsx-a11y/no-static-element-interactions */
/* eslint-disable jsx-a11y/click-events-have-key-events */
import { useCallback, useEffect } from "react";
import { useTranslation } from "react-i18next";
import Head from "next/head";
@@ -54,42 +52,40 @@ export default function LoginPage() {
const handleSelectOrganization = useCallback(
async (organization: Organization) => {
const callbackPort = queryParams.get("callback_port");
if (organization.authEnforced) {
// org has an org-level auth method enabled (e.g. SAML)
// -> logout + redirect to SAML SSO
let samlUrl = `/api/v1/sso/redirect/saml2/organizations/${organization.slug}`;
if (callbackPort) {
samlUrl += `?callback_port=${callbackPort}`;
}
await logout.mutateAsync();
window.open(`/api/v1/sso/redirect/saml2/organizations/${organization.slug}`);
window.open(samlUrl);
window.close();
return;
}
const { token } = await selectOrg.mutateAsync({ organizationId: organization.id });
const callbackPort = queryParams.get("callback_port");
if (callbackPort) {
const privateKey = localStorage.getItem("PRIVATE_KEY");
if (!privateKey) {
createNotification({
text: "Private key not found",
type: "error"
});
}
let error: string | null = null;
if (!user.email) {
createNotification({
text: "User email not found",
type: "error"
});
}
if (!privateKey) error = "Private key not found";
if (!user.email) error = "User email not found";
if (!token) error = "No token found";
if (!token) {
if (error) {
createNotification({
text: "No token found",
text: error,
type: "error"
});
return;
}
const payload = {
@@ -172,33 +168,22 @@ export default function LoginPage() {
<Spinner />
) : (
organizations.data?.map((org) => (
// eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions
<div
onClick={() => handleSelectOrganization(org)}
key={org.id}
className="group flex cursor-pointer items-center justify-between rounded-md bg-mineshaft-700 px-4 py-3 capitalize text-gray-200 shadow-md transition-colors hover:bg-mineshaft-600"
>
<p className="transition-colors">{org.name}</p>
<p className="truncate transition-colors">{org.name}</p>
<FontAwesomeIcon
icon={faArrowRight}
className="text-gray-400 transition-colors group-hover:text-primary-500"
className="text-gray-400 transition-all group-hover:translate-x-2 group-hover:text-primary-500"
/>
</div>
))
)}
</div>
<Button
className="mt-8"
colorSchema="primary"
isLoading={logout.isLoading}
isDisabled={logout.isLoading}
variant="solid"
size="lg"
onClick={handleLogout}
>
Logout
</Button>
</form>
</div>

View File

@@ -454,7 +454,12 @@ const LearningItemSquare = ({
};
const formSchema = yup.object({
name: yup.string().required().label("Project Name").trim().max(64, "Too long, maximum length is 64 characters"),
name: yup
.string()
.required()
.label("Project Name")
.trim()
.max(64, "Too long, maximum length is 64 characters"),
addMembers: yup.bool().required().label("Add Members")
});
@@ -507,7 +512,6 @@ const OrganizationPage = withPermission(
project: { id: newProjectId }
}
} = await createWs.mutateAsync({
organizationSlug: currentOrg.slug,
projectName: name
});
@@ -631,22 +635,21 @@ const OrganizationPage = withPermission(
{orgWorkspaces
.filter((ws) => ws?.name?.toLowerCase().includes(searchFilter.toLowerCase()))
.map((workspace) => (
// eslint-disable-next-line jsx-a11y/no-static-element-interactions, jsx-a11y/click-events-have-key-events
<div
onClick={() => {
router.push(`/project/${workspace.id}/secrets/overview`);
localStorage.setItem("projectData.id", workspace.id);
}}
key={workspace.id}
className="min-w-72 flex h-40 flex-col justify-between rounded-md border border-mineshaft-600 bg-mineshaft-800 p-4"
className="min-w-72 group flex h-40 cursor-pointer flex-col justify-between rounded-md border border-mineshaft-600 bg-mineshaft-800 p-4"
>
<div className="mt-0 truncate text-lg text-mineshaft-100">{workspace.name}</div>
<div className="mt-0 pb-6 text-sm text-mineshaft-300">
{workspace.environments?.length || 0} environments
</div>
<button
type="button"
onClick={() => {
router.push(`/project/${workspace.id}/secrets/overview`);
localStorage.setItem("projectData.id", workspace.id);
}}
>
<div className="group ml-auto w-max cursor-pointer rounded-full border border-mineshaft-600 bg-mineshaft-900 py-2 px-4 text-sm text-mineshaft-300 hover:border-primary-500/80 hover:bg-primary-800/20 hover:text-mineshaft-200">
<button type="button">
<div className="group ml-auto w-max cursor-pointer rounded-full border border-mineshaft-600 bg-mineshaft-900 py-2 px-4 text-sm text-mineshaft-300 transition-all group-hover:border-primary-500/80 group-hover:bg-primary-800/20 group-hover:text-mineshaft-200">
Explore{" "}
<FontAwesomeIcon
icon={faArrowRight}
@@ -693,7 +696,7 @@ const OrganizationPage = withPermission(
<a
target="_blank"
rel="noopener noreferrer"
className="group ml-auto w-max cursor-default rounded-full border border-mineshaft-600 bg-mineshaft-900 py-2 px-4 text-sm text-mineshaft-300 hover:border-primary-500/80 hover:bg-primary-800/20 hover:text-mineshaft-200"
className="group ml-auto w-max cursor-default rounded-full border border-mineshaft-600 bg-mineshaft-900 py-2 px-4 text-sm text-mineshaft-300 transition-all hover:border-primary-500/80 hover:bg-primary-800/20 hover:text-mineshaft-200"
href={feature.link}
>
Learn more{" "}

View File

@@ -9,15 +9,8 @@ class ProjectService {
* @param {String} obj.projectName - name of new project
* @returns {Project} project - new project
*/
static async initProject({
organizationSlug,
projectName
}: {
organizationSlug: string;
projectName: string;
}) {
static async initProject({ projectName }: { projectName: string }) {
return initProjectHelper({
organizationSlug,
projectName
});
}

View File

@@ -189,14 +189,12 @@ export const UserInfoSSOStep = ({
const userOrgs = await fetchOrganizations();
const orgId = userOrgs[0]?.id;
const orgSlug = userOrgs[0]?.slug;
await selectOrganization({
organizationId: orgId
});
const project = await ProjectService.initProject({
organizationSlug: orgSlug,
projectName: "Example Project"
});