feat: fixed review comments

This commit is contained in:
=
2024-12-13 21:38:43 +05:30
parent 9b4e1f561e
commit c57fc5e3f1
37 changed files with 148 additions and 83 deletions
@@ -202,7 +202,7 @@ export async function up(knex: Knex): Promise<void> {
.select("projectId"); .select("projectId");
for (const { projectId } of projectsWithCmek) { for (const { projectId } of projectsWithCmek) {
if (projectId) { if (projectId) {
const newProjectId = await newProject(knex, projectId, ProjectType.Cmek); const newProjectId = await newProject(knex, projectId, ProjectType.KMS);
await knex(TableName.KmsKey) await knex(TableName.KmsKey)
.where({ .where({
isReserved: false, isReserved: false,
@@ -211,14 +211,13 @@ export async function up(knex: Knex): Promise<void> {
.update({ projectId: newProjectId }); .update({ projectId: newProjectId });
await knex(TableName.ProjectSplitBackfillIds).insert({ await knex(TableName.ProjectSplitBackfillIds).insert({
sourceProjectId: projectId, sourceProjectId: projectId,
destinationProjectType: ProjectType.Cmek, destinationProjectType: ProjectType.KMS,
destinationProjectId: newProjectId destinationProjectId: newProjectId
}); });
} }
} }
/* eslint-enable */ /* eslint-enable */
await knex.schema.alterTable(TableName.Project, (t) => { await knex.schema.alterTable(TableName.Project, (t) => {
t.string("type").notNullable().alter(); t.string("type").notNullable().alter();
}); });
+1 -1
View File
@@ -205,5 +205,5 @@ export enum IdentityAuthMethod {
export enum ProjectType { export enum ProjectType {
SecretManager = "secret-manager", SecretManager = "secret-manager",
CertificateManager = "cert-manager", CertificateManager = "cert-manager",
Cmek = "cmek" KMS = "kms"
} }
+1 -1
View File
@@ -25,7 +25,7 @@ export const ProjectsSchema = z.object({
kmsSecretManagerKeyId: z.string().uuid().nullable().optional(), kmsSecretManagerKeyId: z.string().uuid().nullable().optional(),
kmsSecretManagerEncryptedDataKey: zodBuffer.nullable().optional(), kmsSecretManagerEncryptedDataKey: zodBuffer.nullable().optional(),
description: z.string().nullable().optional(), description: z.string().nullable().optional(),
type: z.string().nullable().optional() type: z.string()
}); });
export type TProjects = z.infer<typeof ProjectsSchema>; export type TProjects = z.infer<typeof ProjectsSchema>;
@@ -328,7 +328,7 @@ export const registerIdentityRouter = async (server: FastifyZodProvider) => {
identity: IdentitiesSchema.pick({ name: true, id: true }).extend({ identity: IdentitiesSchema.pick({ name: true, id: true }).extend({
authMethods: z.array(z.string()) authMethods: z.array(z.string())
}), }),
project: SanitizedProjectSchema.pick({ name: true, id: true }) project: SanitizedProjectSchema.pick({ name: true, id: true, type: true })
}) })
) )
}) })
@@ -137,7 +137,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
.enum(["true", "false"]) .enum(["true", "false"])
.default("false") .default("false")
.transform((value) => value === "true"), .transform((value) => value === "true"),
type: z.enum([ProjectType.SecretManager, ProjectType.Cmek, ProjectType.CertificateManager, "all"]).optional() type: z.enum([ProjectType.SecretManager, ProjectType.KMS, ProjectType.CertificateManager, "all"]).optional()
}), }),
response: { response: {
200: z.object({ 200: z.object({
@@ -77,7 +77,9 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
} }
], ],
params: z.object({ params: z.object({
organizationId: z.string().trim().describe(ORGANIZATIONS.GET_PROJECTS.organizationId), organizationId: z.string().trim().describe(ORGANIZATIONS.GET_PROJECTS.organizationId)
}),
querystring: z.object({
type: z.nativeEnum(ProjectType).optional().describe(ORGANIZATIONS.GET_PROJECTS.type) type: z.nativeEnum(ProjectType).optional().describe(ORGANIZATIONS.GET_PROJECTS.type)
}), }),
response: { response: {
@@ -106,7 +108,8 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId, actorOrgId: req.permission.orgId,
actorAuthMethod: req.permission.authMethod, actorAuthMethod: req.permission.authMethod,
orgId: req.params.organizationId orgId: req.params.organizationId,
type: req.query.type
}); });
return { workspaces }; return { workspaces };
@@ -283,7 +286,7 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
lastName: true, lastName: true,
id: true id: true
}).merge(UserEncryptionKeysSchema.pick({ publicKey: true })), }).merge(UserEncryptionKeysSchema.pick({ publicKey: true })),
project: ProjectsSchema.pick({ name: true, id: true }), project: ProjectsSchema.pick({ name: true, id: true, type: true }),
roles: z.array( roles: z.array(
z.object({ z.object({
id: z.string(), id: z.string(),
+7 -7
View File
@@ -29,7 +29,7 @@ export type TCmekServiceFactory = ReturnType<typeof cmekServiceFactory>;
export const cmekServiceFactory = ({ kmsService, kmsDAL, permissionService, projectDAL }: TCmekServiceFactoryDep) => { export const cmekServiceFactory = ({ kmsService, kmsDAL, permissionService, projectDAL }: TCmekServiceFactoryDep) => {
const createCmek = async ({ projectId: preSplitProjectId, ...dto }: TCreateCmekDTO, actor: OrgServiceActor) => { const createCmek = async ({ projectId: preSplitProjectId, ...dto }: TCreateCmekDTO, actor: OrgServiceActor) => {
let projectId = preSplitProjectId; let projectId = preSplitProjectId;
const cmekProjectFromSplit = await projectDAL.getProjectFromSplitId(projectId, ProjectType.Cmek); const cmekProjectFromSplit = await projectDAL.getProjectFromSplitId(projectId, ProjectType.KMS);
if (cmekProjectFromSplit) { if (cmekProjectFromSplit) {
projectId = cmekProjectFromSplit.id; projectId = cmekProjectFromSplit.id;
} }
@@ -41,7 +41,7 @@ export const cmekServiceFactory = ({ kmsService, kmsDAL, permissionService, proj
actor.authMethod, actor.authMethod,
actor.orgId actor.orgId
); );
ForbidOnInvalidProjectType(ProjectType.Cmek); ForbidOnInvalidProjectType(ProjectType.KMS);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionCmekActions.Create, ProjectPermissionSub.Cmek); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionCmekActions.Create, ProjectPermissionSub.Cmek);
const cmek = await kmsService.generateKmsKey({ const cmek = await kmsService.generateKmsKey({
@@ -67,7 +67,7 @@ export const cmekServiceFactory = ({ kmsService, kmsDAL, permissionService, proj
actor.authMethod, actor.authMethod,
actor.orgId actor.orgId
); );
ForbidOnInvalidProjectType(ProjectType.Cmek); ForbidOnInvalidProjectType(ProjectType.KMS);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionCmekActions.Edit, ProjectPermissionSub.Cmek); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionCmekActions.Edit, ProjectPermissionSub.Cmek);
@@ -90,7 +90,7 @@ export const cmekServiceFactory = ({ kmsService, kmsDAL, permissionService, proj
actor.authMethod, actor.authMethod,
actor.orgId actor.orgId
); );
ForbidOnInvalidProjectType(ProjectType.Cmek); ForbidOnInvalidProjectType(ProjectType.KMS);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionCmekActions.Delete, ProjectPermissionSub.Cmek); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionCmekActions.Delete, ProjectPermissionSub.Cmek);
@@ -104,7 +104,7 @@ export const cmekServiceFactory = ({ kmsService, kmsDAL, permissionService, proj
actor: OrgServiceActor actor: OrgServiceActor
) => { ) => {
let projectId = preSplitProjectId; let projectId = preSplitProjectId;
const cmekProjectFromSplit = await projectDAL.getProjectFromSplitId(preSplitProjectId, ProjectType.Cmek); const cmekProjectFromSplit = await projectDAL.getProjectFromSplitId(preSplitProjectId, ProjectType.KMS);
if (cmekProjectFromSplit) { if (cmekProjectFromSplit) {
projectId = cmekProjectFromSplit.id; projectId = cmekProjectFromSplit.id;
} }
@@ -141,7 +141,7 @@ export const cmekServiceFactory = ({ kmsService, kmsDAL, permissionService, proj
actor.orgId actor.orgId
); );
ForbidOnInvalidProjectType(ProjectType.Cmek); ForbidOnInvalidProjectType(ProjectType.KMS);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionCmekActions.Encrypt, ProjectPermissionSub.Cmek); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionCmekActions.Encrypt, ProjectPermissionSub.Cmek);
const encrypt = await kmsService.encryptWithKmsKey({ kmsId: keyId }); const encrypt = await kmsService.encryptWithKmsKey({ kmsId: keyId });
@@ -167,7 +167,7 @@ export const cmekServiceFactory = ({ kmsService, kmsDAL, permissionService, proj
actor.authMethod, actor.authMethod,
actor.orgId actor.orgId
); );
ForbidOnInvalidProjectType(ProjectType.Cmek); ForbidOnInvalidProjectType(ProjectType.KMS);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionCmekActions.Decrypt, ProjectPermissionSub.Cmek); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionCmekActions.Decrypt, ProjectPermissionSub.Cmek);
@@ -102,6 +102,7 @@ export const identityProjectDALFactory = (db: TDbClient) => {
db.ref("temporaryAccessEndTime").withSchema(TableName.IdentityProjectMembershipRole), db.ref("temporaryAccessEndTime").withSchema(TableName.IdentityProjectMembershipRole),
db.ref("projectId").withSchema(TableName.IdentityProjectMembership), db.ref("projectId").withSchema(TableName.IdentityProjectMembership),
db.ref("name").as("projectName").withSchema(TableName.Project), db.ref("name").as("projectName").withSchema(TableName.Project),
db.ref("type").as("projectType").withSchema(TableName.Project),
db.ref("id").as("uaId").withSchema(TableName.IdentityUniversalAuth), db.ref("id").as("uaId").withSchema(TableName.IdentityUniversalAuth),
db.ref("id").as("gcpId").withSchema(TableName.IdentityGcpAuth), db.ref("id").as("gcpId").withSchema(TableName.IdentityGcpAuth),
db.ref("id").as("awsId").withSchema(TableName.IdentityAwsAuth), db.ref("id").as("awsId").withSchema(TableName.IdentityAwsAuth),
@@ -126,7 +127,8 @@ export const identityProjectDALFactory = (db: TDbClient) => {
createdAt, createdAt,
updatedAt, updatedAt,
projectId, projectId,
projectName projectName,
projectType
}) => ({ }) => ({
id, id,
identityId, identityId,
@@ -147,7 +149,8 @@ export const identityProjectDALFactory = (db: TDbClient) => {
}, },
project: { project: {
id: projectId, id: projectId,
name: projectName name: projectName,
type: projectType
} }
}), }),
key: "id", key: "id",
+1 -1
View File
@@ -1,7 +1,7 @@
import { ProjectType } from "@app/db/schemas";
import { TOrgPermission } from "@app/lib/types"; import { TOrgPermission } from "@app/lib/types";
import { ActorAuthMethod, ActorType, MfaMethod } from "../auth/auth-type"; import { ActorAuthMethod, ActorType, MfaMethod } from "../auth/auth-type";
import { ProjectType } from "@app/db/schemas";
export type TUpdateOrgMembershipDTO = { export type TUpdateOrgMembershipDTO = {
userId: string; userId: string;
@@ -217,20 +217,33 @@ export const projectMembershipDALFactory = (db: TDbClient) => {
db.ref("temporaryAccessStartTime").withSchema(TableName.ProjectUserMembershipRole), db.ref("temporaryAccessStartTime").withSchema(TableName.ProjectUserMembershipRole),
db.ref("temporaryAccessEndTime").withSchema(TableName.ProjectUserMembershipRole), db.ref("temporaryAccessEndTime").withSchema(TableName.ProjectUserMembershipRole),
db.ref("name").as("projectName").withSchema(TableName.Project), db.ref("name").as("projectName").withSchema(TableName.Project),
db.ref("id").as("projectId").withSchema(TableName.Project) db.ref("id").as("projectId").withSchema(TableName.Project),
db.ref("type").as("projectType").withSchema(TableName.Project)
) )
.where({ isGhost: false }); .where({ isGhost: false });
const members = sqlNestRelationships({ const members = sqlNestRelationships({
data: docs, data: docs,
parentMapper: ({ email, firstName, username, lastName, publicKey, isGhost, id, projectId, projectName }) => ({ parentMapper: ({
email,
firstName,
username,
lastName,
publicKey,
isGhost,
id,
projectId,
projectName,
projectType
}) => ({
id, id,
userId, userId,
projectId, projectId,
user: { email, username, firstName, lastName, id: userId, publicKey, isGhost }, user: { email, username, firstName, lastName, id: userId, publicKey, isGhost },
project: { project: {
id: projectId, id: projectId,
name: projectName name: projectName,
type: projectType
} }
}), }),
key: "id", key: "id",
@@ -32,7 +32,7 @@ import {
useSubscription, useSubscription,
useUser useUser
} from "@app/context"; } from "@app/context";
import { getWorkspaceHomePage } from "@app/helpers/workspace"; import { getProjectHomePage } from "@app/helpers/project";
import { import {
fetchOrgUsers, fetchOrgUsers,
useAddUserToWsNonE2EE, useAddUserToWsNonE2EE,
@@ -120,9 +120,7 @@ const NewProjectForm = ({ onOpenChange, projectType }: NewProjectFormProps) => {
if (!user) return; if (!user) return;
try { try {
const { const {
data: { data: { project }
project
}
} = await createWs.mutateAsync({ } = await createWs.mutateAsync({
projectName: name, projectName: name,
projectDescription: description, projectDescription: description,
@@ -130,7 +128,7 @@ const NewProjectForm = ({ onOpenChange, projectType }: NewProjectFormProps) => {
template, template,
type: projectType type: projectType
}); });
const { id: newProjectId } = project const { id: newProjectId } = project;
if (addMembers) { if (addMembers) {
const orgUsers = await fetchOrgUsers(currentOrg.id); const orgUsers = await fetchOrgUsers(currentOrg.id);
@@ -150,7 +148,7 @@ const NewProjectForm = ({ onOpenChange, projectType }: NewProjectFormProps) => {
createNotification({ text: "Project created", type: "success" }); createNotification({ text: "Project created", type: "success" });
reset(); reset();
onOpenChange(false); onOpenChange(false);
router.push(getWorkspaceHomePage(project)); router.push(getProjectHomePage(project));
} catch (err) { } catch (err) {
console.error(err); console.error(err);
createNotification({ text: "Failed to create project", type: "error" }); createNotification({ text: "Failed to create project", type: "error" });
+21 -3
View File
@@ -1,6 +1,6 @@
import { apiRequest } from "@app/config/request"; import { apiRequest } from "@app/config/request";
import { createWorkspace } from "@app/hooks/api/workspace/queries"; import { createWorkspace } from "@app/hooks/api/workspace/queries";
import { ProjectType } from "@app/hooks/api/workspace/types"; import { ProjectType, Workspace } from "@app/hooks/api/workspace/types";
const secretsToBeAdded = [ const secretsToBeAdded = [
{ {
@@ -37,7 +37,7 @@ const secretsToBeAdded = [
* Create and initialize a new project in organization with id [organizationId] * Create and initialize a new project in organization with id [organizationId]
* Note: current user should be a member of the organization * Note: current user should be a member of the organization
*/ */
const initProjectHelper = async ({ projectName }: { projectName: string }) => { export const initProjectHelper = async ({ projectName }: { projectName: string }) => {
// create new project // create new project
const { const {
data: { project } data: { project }
@@ -61,4 +61,22 @@ const initProjectHelper = async ({ projectName }: { projectName: string }) => {
return project; return project;
}; };
export { initProjectHelper }; export const getProjectHomePage = (workspace: Workspace) => {
if (workspace.type === ProjectType.SecretManager) {
return `/${workspace.type}/${workspace.id}/secrets/overview`;
}
if (workspace.type === ProjectType.CertificateManager) {
return `/${workspace.type}/${workspace.id}/certificates`;
}
return `/${workspace.type}/${workspace.id}/kms`;
};
export const getProjectTitle = (type: ProjectType) => {
const titleConvert = {
[ProjectType.SecretManager]: "Secret Management",
[ProjectType.KMS]: "Key Management",
[ProjectType.CertificateManager]: "Cert Management"
};
return titleConvert[type];
};
-12
View File
@@ -1,12 +0,0 @@
import { Workspace } from "@app/hooks/api/types";
import { ProjectType } from "@app/hooks/api/workspace/types";
export const getWorkspaceHomePage = (workspace: Workspace) => {
if (workspace.type === ProjectType.SecretManager) {
return `/${workspace.type}/${workspace.id}/secrets/overview`;
}
if (workspace.type === ProjectType.CertificateManager) {
return `/${workspace.type}/${workspace.id}/certificates`;
}
return `/${workspace.type}/${workspace.id}/kms`;
};
+1 -1
View File
@@ -47,7 +47,7 @@ export type IdentityMembershipOrg = {
export type IdentityMembership = { export type IdentityMembership = {
id: string; id: string;
identity: Identity; identity: Identity;
project: Pick<Workspace, "id" | "name">; project: Pick<Workspace, "id" | "name" | "type">;
roles: Array< roles: Array<
{ {
id: string; id: string;
+2 -1
View File
@@ -1,6 +1,6 @@
import { MfaMethod } from "../auth/types"; import { MfaMethod } from "../auth/types";
import { UserWsKeyPair } from "../keys/types"; import { UserWsKeyPair } from "../keys/types";
import { ProjectUserMembershipTemporaryMode } from "../workspace/types"; import { ProjectType, ProjectUserMembershipTemporaryMode } from "../workspace/types";
export enum AuthMethod { export enum AuthMethod {
EMAIL = "email", EMAIL = "email",
@@ -95,6 +95,7 @@ export type TWorkspaceUser = {
project: { project: {
id: string; id: string;
name: string; name: string;
type: ProjectType;
}; };
inviteEmail: string; inviteEmail: string;
organization: string; organization: string;
+1 -1
View File
@@ -11,7 +11,7 @@ export enum ProjectVersion {
export enum ProjectType { export enum ProjectType {
SecretManager = "secret-manager", SecretManager = "secret-manager",
CertificateManager = "cert-manager", CertificateManager = "cert-manager",
Cmek = "cmek" KMS = "kms"
} }
export enum ProjectUserMembershipTemporaryMode { export enum ProjectUserMembershipTemporaryMode {
+5 -5
View File
@@ -352,7 +352,7 @@ export const AppLayout = ({ children }: LayoutProps) => {
)} )}
icon="system-outline-165-view-carousel" icon="system-outline-165-view-carousel"
> >
Secret Manager Secret Management
</MenuItem> </MenuItem>
</a> </a>
</Link> </Link>
@@ -367,17 +367,17 @@ export const AppLayout = ({ children }: LayoutProps) => {
)} )}
icon="system-outline-165-view-carousel" icon="system-outline-165-view-carousel"
> >
Cert Manager Cert Management
</MenuItem> </MenuItem>
</a> </a>
</Link> </Link>
<Link href={`/org/${currentOrg?.id}/${ProjectType.Cmek}/overview`} passHref> <Link href={`/org/${currentOrg?.id}/${ProjectType.KMS}/overview`} passHref>
<a> <a>
<MenuItem <MenuItem
isSelected={router.asPath.includes(`/${ProjectType.Cmek}/overview`)} isSelected={router.asPath.includes(`/${ProjectType.KMS}/overview`)}
icon="system-outline-165-view-carousel" icon="system-outline-165-view-carousel"
> >
Cmek Key Management
</MenuItem> </MenuItem>
</a> </a>
</Link> </Link>
@@ -16,7 +16,7 @@ import {
useSubscription, useSubscription,
useWorkspace useWorkspace
} from "@app/context"; } from "@app/context";
import { getWorkspaceHomePage } from "@app/helpers/workspace"; import { getProjectHomePage } from "@app/helpers/project";
import { usePopUp } from "@app/hooks"; import { usePopUp } from "@app/hooks";
import { useUpdateUserProjectFavorites } from "@app/hooks/api/users/mutation"; import { useUpdateUserProjectFavorites } from "@app/hooks/api/users/mutation";
import { useGetUserProjectFavorites } from "@app/hooks/api/users/queries"; import { useGetUserProjectFavorites } from "@app/hooks/api/users/queries";
@@ -191,7 +191,7 @@ export const ProjectSelect = () => {
// todo(akhi): this is not using react query because react query in overview is throwing error when envs are not exact same count // todo(akhi): this is not using react query because react query in overview is throwing error when envs are not exact same count
// to reproduce change this back to router.push and switch between two projects with different env count // to reproduce change this back to router.push and switch between two projects with different env count
// look into this on dashboard revamp // look into this on dashboard revamp
window.location.assign(getWorkspaceHomePage(project)); window.location.assign(getProjectHomePage(project));
}} }}
options={options} options={options}
components={{ components={{
@@ -31,7 +31,7 @@ export const ProjectSidebarItem = () => {
const isSecretManager = currentWorkspace?.type === ProjectType.SecretManager; const isSecretManager = currentWorkspace?.type === ProjectType.SecretManager;
const isCertManager = currentWorkspace?.type === ProjectType.CertificateManager; const isCertManager = currentWorkspace?.type === ProjectType.CertificateManager;
const isCmek = currentWorkspace?.type === ProjectType.Cmek; const isCmek = currentWorkspace?.type === ProjectType.KMS;
return ( return (
<Menu> <Menu>
@@ -71,10 +71,10 @@ export const ProjectSidebarItem = () => {
</Link> </Link>
)} )}
{isCmek && ( {isCmek && (
<Link href={`/${ProjectType.Cmek}/${currentWorkspace?.id}/kms`} passHref> <Link href={`/${ProjectType.KMS}/${currentWorkspace?.id}/kms`} passHref>
<a> <a>
<MenuItem <MenuItem
isSelected={router.asPath === `/${ProjectType.Cmek}/${currentWorkspace?.id}/kms`} isSelected={router.asPath === `/${ProjectType.KMS}/${currentWorkspace?.id}/kms`}
icon="system-outline-90-lock-closed" icon="system-outline-90-lock-closed"
> >
Key Management Key Management
@@ -2,7 +2,7 @@ import { ProjectType } from "@app/hooks/api/workspace/types";
import { ProductOverview } from "../secret-manager/overview"; import { ProductOverview } from "../secret-manager/overview";
const CmekManagerOverviewPage = () => <ProductOverview type={ProjectType.Cmek} />; const CmekManagerOverviewPage = () => <ProductOverview type={ProjectType.KMS} />;
Object.assign(CmekManagerOverviewPage, { requireAuth: true }); Object.assign(CmekManagerOverviewPage, { requireAuth: true });
@@ -2,6 +2,7 @@ import { useEffect } from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { useOrganization } from "@app/context"; import { useOrganization } from "@app/context";
import { ProjectType } from "@app/hooks/api/workspace/types";
// #TODO: Update all the workspaceIds // #TODO: Update all the workspaceIds
const OrganizationPage = () => { const OrganizationPage = () => {
@@ -9,7 +10,7 @@ const OrganizationPage = () => {
const { currentOrg } = useOrganization(); const { currentOrg } = useOrganization();
useEffect(() => { useEffect(() => {
if (router.isReady && currentOrg?.id) { if (router.isReady && currentOrg?.id) {
router.push(`/org/${currentOrg?.id}/secret-manager/overview`); router.push(`/org/${currentOrg?.id}/${ProjectType.SecretManager}/overview`);
} }
}, [router.isReady, currentOrg?.id]); }, [router.isReady, currentOrg?.id]);
@@ -38,7 +38,7 @@ import {
useOrganization, useOrganization,
useSubscription useSubscription
} from "@app/context"; } from "@app/context";
import { getWorkspaceHomePage } from "@app/helpers/workspace"; import { getProjectHomePage } from "@app/helpers/project";
import { usePagination, useResetPageHelper } from "@app/hooks"; import { usePagination, useResetPageHelper } from "@app/hooks";
import { useGetUserWorkspaces } from "@app/hooks/api"; import { useGetUserWorkspaces } from "@app/hooks/api";
import { OrderByDirection } from "@app/hooks/api/generic/types"; import { OrderByDirection } from "@app/hooks/api/generic/types";
@@ -60,9 +60,17 @@ enum ProjectOrderBy {
} }
const formatTitle = (type: ProjectType) => { const formatTitle = (type: ProjectType) => {
if (type === ProjectType.SecretManager) return "Secret Managers"; if (type === ProjectType.SecretManager) return "Secret Management";
if (type === ProjectType.CertificateManager) return "Cert Managers"; if (type === ProjectType.CertificateManager) return "Cert Management";
return "Cmek"; return "Key Management";
};
const formatDescription = (type: ProjectType) => {
if (type === ProjectType.SecretManager)
return "Securely store, manage, and rotate various application secrets, such as database credentials, API keys, etc.";
if (type === ProjectType.CertificateManager)
return "Manage your PKI infrastructure and issue digital certificates for services, applications, and devices.";
return "Centralize the management of keys for cryptographic operations, such as encryption and decryption.";
}; };
type Props = { type Props = {
@@ -182,7 +190,7 @@ export const ProductOverview = ({ type }: Props) => {
// eslint-disable-next-line jsx-a11y/no-static-element-interactions, jsx-a11y/click-events-have-key-events // eslint-disable-next-line jsx-a11y/no-static-element-interactions, jsx-a11y/click-events-have-key-events
<div <div
onClick={() => { onClick={() => {
router.push(getWorkspaceHomePage(workspace)); router.push(getProjectHomePage(workspace));
localStorage.setItem("projectData.id", workspace.id); localStorage.setItem("projectData.id", workspace.id);
}} }}
key={workspace.id} key={workspace.id}
@@ -246,7 +254,7 @@ export const ProductOverview = ({ type }: Props) => {
// eslint-disable-next-line jsx-a11y/no-static-element-interactions, jsx-a11y/click-events-have-key-events // eslint-disable-next-line jsx-a11y/no-static-element-interactions, jsx-a11y/click-events-have-key-events
<div <div
onClick={() => { onClick={() => {
router.push(getWorkspaceHomePage(workspace)); router.push(getProjectHomePage(workspace));
localStorage.setItem("projectData.id", workspace.id); localStorage.setItem("projectData.id", workspace.id);
}} }}
key={workspace.id} key={workspace.id}
@@ -382,9 +390,12 @@ export const ProductOverview = ({ type }: Props) => {
</div> </div>
</div> </div>
)} )}
<div className="mb-4 flex flex-col items-start justify-start px-6 py-6 pb-0 text-3xl"> <div className="mb-4 flex flex-col items-start justify-start px-6 py-6 pb-0">
<div className="flex w-full justify-between"> <div className="flex w-full justify-between">
<p className="mr-4 font-semibold text-white">{formatTitle(type)}</p> <p className="mr-4 text-3xl font-semibold text-white">{formatTitle(type)}</p>
</div>
<div>
<p className="mr-4 mt-2 text-gray-400">{formatDescription(type)}</p>
</div> </div>
<div className="mt-6 flex w-full flex-row"> <div className="mt-6 flex w-full flex-row">
<Input <Input
@@ -5,7 +5,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { format } from "date-fns"; import { format } from "date-fns";
import { createNotification } from "@app/components/notifications"; import { createNotification } from "@app/components/notifications";
import { IconButton, Td, Tooltip, Tr } from "@app/components/v2"; import { IconButton, Tag, Td, Tooltip, Tr } from "@app/components/v2";
import { useWorkspace } from "@app/context"; import { useWorkspace } from "@app/context";
import { IdentityMembership } from "@app/hooks/api/identities/types"; import { IdentityMembership } from "@app/hooks/api/identities/types";
import { ProjectMembershipRole } from "@app/hooks/api/roles/types"; import { ProjectMembershipRole } from "@app/hooks/api/roles/types";
@@ -33,7 +33,7 @@ export const IdentityProjectRow = ({
membership: { id, createdAt, identity, project, roles }, membership: { id, createdAt, identity, project, roles },
handlePopUpOpen handlePopUpOpen
}: Props) => { }: Props) => {
const { workspaces,currentWorkspace } = useWorkspace(); const { workspaces, currentWorkspace } = useWorkspace();
const router = useRouter(); const router = useRouter();
const isAccessible = useMemo(() => { const isAccessible = useMemo(() => {
@@ -52,7 +52,9 @@ export const IdentityProjectRow = ({
key={`identity-project-membership-${id}`} key={`identity-project-membership-${id}`}
onClick={() => { onClick={() => {
if (isAccessible) { if (isAccessible) {
router.push(`/${currentWorkspace?.type}/${project.id}/members?selectedTab=${TabSections.Identities}`); router.push(
`/${currentWorkspace?.type}/${project.id}/members?selectedTab=${TabSections.Identities}`
);
return; return;
} }
@@ -63,6 +65,9 @@ export const IdentityProjectRow = ({
}} }}
> >
<Td className="max-w-0 truncate">{project.name}</Td> <Td className="max-w-0 truncate">{project.name}</Td>
<Td>
<Tag size="xs">{project.type}</Tag>
</Td>
<Td>{`${formatRoleName(roles[0].role, roles[0].customRoleName)}${ <Td>{`${formatRoleName(roles[0].role, roles[0].customRoleName)}${
roles.length > 1 ? ` (+${roles.length - 1})` : "" roles.length > 1 ? ` (+${roles.length - 1})` : ""
}`}</Td> }`}</Td>
@@ -106,6 +106,7 @@ export const IdentityProjectsTable = ({ identityId, handlePopUpOpen }: Props) =>
</div> </div>
</Th> </Th>
<Th>Type</Th>
<Th>Role</Th> <Th>Role</Th>
<Th>Added On</Th> <Th>Added On</Th>
<Th className="w-5" /> <Th className="w-5" />
@@ -4,7 +4,7 @@ import { faTrash } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { createNotification } from "@app/components/notifications"; import { createNotification } from "@app/components/notifications";
import { IconButton, Td, Tooltip, Tr } from "@app/components/v2"; import { IconButton, Tag, Td, Tooltip, Tr } from "@app/components/v2";
import { useWorkspace } from "@app/context"; import { useWorkspace } from "@app/context";
import { ProjectMembershipRole } from "@app/hooks/api/roles/types"; import { ProjectMembershipRole } from "@app/hooks/api/roles/types";
import { TWorkspaceUser } from "@app/hooks/api/types"; import { TWorkspaceUser } from "@app/hooks/api/types";
@@ -59,6 +59,9 @@ export const UserProjectRow = ({
}} }}
> >
<Td className="max-w-0 truncate">{project.name}</Td> <Td className="max-w-0 truncate">{project.name}</Td>
<Td>
<Tag size="xs">{project.type}</Tag>
</Td>
<Td>{`${formatRoleName(roles[0].role, roles[0].customRoleName)}${ <Td>{`${formatRoleName(roles[0].role, roles[0].customRoleName)}${
roles.length > 1 ? ` (+${roles.length - 1})` : "" roles.length > 1 ? ` (+${roles.length - 1})` : ""
}`}</Td> }`}</Td>
@@ -108,6 +108,7 @@ export const UserProjectsTable = ({ membershipId, handlePopUpOpen }: Props) => {
</IconButton> </IconButton>
</div> </div>
</Th> </Th>
<Th>Type</Th>
<Th>Role</Th> <Th>Role</Th>
<Th className="w-5" /> <Th className="w-5" />
</Tr> </Tr>
@@ -8,6 +8,7 @@ import { createNotification } from "@app/components/notifications";
import { ProjectPermissionCan } from "@app/components/permissions"; import { ProjectPermissionCan } from "@app/components/permissions";
import { Button, DeleteActionModal, EmptyState, Spinner } from "@app/components/v2"; import { Button, DeleteActionModal, EmptyState, Spinner } from "@app/components/v2";
import { ProjectPermissionActions, ProjectPermissionSub, useWorkspace } from "@app/context"; import { ProjectPermissionActions, ProjectPermissionSub, useWorkspace } from "@app/context";
import { getProjectTitle } from "@app/helpers/project";
import { withProjectPermission } from "@app/hoc"; import { withProjectPermission } from "@app/hoc";
import { usePopUp } from "@app/hooks"; import { usePopUp } from "@app/hooks";
import { import {
@@ -77,11 +78,14 @@ export const IdentityDetailsPage = withProjectPermission(
type="submit" type="submit"
leftIcon={<FontAwesomeIcon icon={faChevronLeft} />} leftIcon={<FontAwesomeIcon icon={faChevronLeft} />}
onClick={() => { onClick={() => {
router.push(`/${currentWorkspace?.type}/${workspaceId}/members?selectedTab=identities`); router.push(
`/${currentWorkspace?.type}/${workspaceId}/members?selectedTab=identities`
);
}} }}
className="mb-4" className="mb-4"
> >
Project Access Control {currentWorkspace?.type ? getProjectTitle(currentWorkspace?.type) : "Project"} Access
Access Control
</Button> </Button>
</div> </div>
{identityMembershipDetails ? ( {identityMembershipDetails ? (
@@ -18,6 +18,7 @@ import {
useOrganization, useOrganization,
useWorkspace useWorkspace
} from "@app/context"; } from "@app/context";
import { getProjectTitle } from "@app/helpers/project";
import { withProjectPermission } from "@app/hoc"; import { withProjectPermission } from "@app/hoc";
import { usePopUp } from "@app/hooks"; import { usePopUp } from "@app/hooks";
import { useDeleteUserFromWorkspace, useGetWorkspaceUserDetails } from "@app/hooks/api"; import { useDeleteUserFromWorkspace, useGetWorkspaceUserDetails } from "@app/hooks/api";
@@ -89,7 +90,8 @@ export const MemberDetailsPage = withProjectPermission(
}} }}
className="mb-4" className="mb-4"
> >
Project Access Control {currentWorkspace?.type ? getProjectTitle(currentWorkspace?.type) : "Project"} Access
Control
</Button> </Button>
</div> </div>
{membershipDetails ? ( {membershipDetails ? (
@@ -3,8 +3,10 @@ import { useEffect, useState } from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { Tab, TabList, TabPanel, Tabs } from "@app/components/v2"; import { Tab, TabList, TabPanel, Tabs } from "@app/components/v2";
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/context"; import { ProjectPermissionActions, ProjectPermissionSub, useWorkspace } from "@app/context";
import { getProjectTitle } from "@app/helpers/project";
import { withProjectPermission } from "@app/hoc"; import { withProjectPermission } from "@app/hoc";
import { ProjectType } from "@app/hooks/api/workspace/types";
import { isTabSection, TabSections } from "../Types"; import { isTabSection, TabSections } from "../Types";
import { import {
@@ -18,6 +20,7 @@ import {
export const MembersPage = withProjectPermission( export const MembersPage = withProjectPermission(
() => { () => {
const router = useRouter(); const router = useRouter();
const { currentWorkspace } = useWorkspace();
const { query } = router; const { query } = router;
const selectedTab = query.selectedTab as string; const selectedTab = query.selectedTab as string;
const [activeTab, setActiveTab] = useState<TabSections>(TabSections.Member); const [activeTab, setActiveTab] = useState<TabSections>(TabSections.Member);
@@ -38,7 +41,10 @@ export const MembersPage = withProjectPermission(
return ( return (
<div className="container mx-auto flex flex-col justify-between bg-bunker-800 text-white"> <div className="container mx-auto flex flex-col justify-between bg-bunker-800 text-white">
<div className="mx-auto mb-6 w-full max-w-7xl py-6 px-6"> <div className="mx-auto mb-6 w-full max-w-7xl py-6 px-6">
<p className="mr-4 mb-4 text-3xl font-semibold text-white">Project Access Control</p> <p className="mr-4 mb-4 text-3xl font-semibold text-white">
{currentWorkspace?.type ? getProjectTitle(currentWorkspace?.type) : "Project"} Access
Access Control
</p>
<Tabs value={activeTab} onValueChange={updateSelectedTab}> <Tabs value={activeTab} onValueChange={updateSelectedTab}>
<TabList> <TabList>
<Tab value={TabSections.Member}>Users</Tab> <Tab value={TabSections.Member}>Users</Tab>
@@ -60,9 +66,11 @@ export const MembersPage = withProjectPermission(
<TabPanel value={TabSections.Identities}> <TabPanel value={TabSections.Identities}>
<IdentityTab /> <IdentityTab />
</TabPanel> </TabPanel>
<TabPanel value={TabSections.ServiceTokens}> {currentWorkspace?.type === ProjectType.SecretManager && (
<ServiceTokenTab /> <TabPanel value={TabSections.ServiceTokens}>
</TabPanel> <ServiceTokenTab />
</TabPanel>
)}
<TabPanel value={TabSections.Roles}> <TabPanel value={TabSections.Roles}>
<ProjectRoleListTab /> <ProjectRoleListTab />
</TabPanel> </TabPanel>
@@ -23,7 +23,11 @@ export const ProjectSettingsPage = () => {
currentWorkspace?.type !== ProjectType.SecretManager currentWorkspace?.type !== ProjectType.SecretManager
}, },
{ name: "Workflow Integrations", key: "tab-workflow-integrations" }, { name: "Workflow Integrations", key: "tab-workflow-integrations" },
{ name: "Webhooks", key: "tab-project-webhooks" } {
name: "Webhooks",
key: "tab-project-webhooks",
isHidden: currentWorkspace?.type !== ProjectType.SecretManager
}
]; ];
return ( return (
@@ -64,9 +68,11 @@ export const ProjectSettingsPage = () => {
<Tab.Panel> <Tab.Panel>
<WorkflowIntegrationTab /> <WorkflowIntegrationTab />
</Tab.Panel> </Tab.Panel>
<Tab.Panel> {currentWorkspace?.type === ProjectType.SecretManager && (
<WebhooksTab /> <Tab.Panel>
</Tab.Panel> <WebhooksTab />
</Tab.Panel>
)}
</Tab.Panels> </Tab.Panels>
</Tab.Group> </Tab.Group>
</div> </div>