Merge pull request #1338 from akhilmhdh/feat/blind-index-fix

feat: changed blind index banner for everyone
This commit is contained in:
Maidul Islam
2024-01-28 14:37:31 -05:00
committed by GitHub
27 changed files with 140 additions and 82 deletions
+3 -2
View File
@@ -2,6 +2,7 @@ import { z } from "zod";
import { AuditLogsSchema, SecretSnapshotsSchema } from "@app/db/schemas"; import { AuditLogsSchema, SecretSnapshotsSchema } from "@app/db/schemas";
import { EventType, UserAgentType } from "@app/ee/services/audit-log/audit-log-types"; import { EventType, UserAgentType } from "@app/ee/services/audit-log/audit-log-types";
import { removeTrailingSlash } from "@app/lib/fn";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type"; import { AuthMode } from "@app/services/auth/auth-type";
@@ -15,7 +16,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
}), }),
querystring: z.object({ querystring: z.object({
environment: z.string().trim(), environment: z.string().trim(),
path: z.string().trim().default("/"), path: z.string().trim().default("/").transform(removeTrailingSlash),
offset: z.coerce.number().default(0), offset: z.coerce.number().default(0),
limit: z.coerce.number().default(20) limit: z.coerce.number().default(20)
}), }),
@@ -46,7 +47,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
}), }),
querystring: z.object({ querystring: z.object({
environment: z.string().trim(), environment: z.string().trim(),
path: z.string().trim().default("/") path: z.string().trim().default("/").transform(removeTrailingSlash)
}), }),
response: { response: {
200: z.object({ 200: z.object({
@@ -1,6 +1,7 @@
import { z } from "zod"; import { z } from "zod";
import { SecretRotationOutputsSchema, SecretRotationsSchema, SecretsSchema } from "@app/db/schemas"; import { SecretRotationOutputsSchema, SecretRotationsSchema, SecretsSchema } from "@app/db/schemas";
import { removeTrailingSlash } from "@app/lib/fn";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type"; import { AuthMode } from "@app/services/auth/auth-type";
@@ -11,7 +12,7 @@ export const registerSecretRotationRouter = async (server: FastifyZodProvider) =
schema: { schema: {
body: z.object({ body: z.object({
workspaceId: z.string().trim(), workspaceId: z.string().trim(),
secretPath: z.string().trim(), secretPath: z.string().trim().transform(removeTrailingSlash),
environment: z.string().trim(), environment: z.string().trim(),
interval: z.number().min(1), interval: z.number().min(1),
provider: z.string().trim(), provider: z.string().trim(),
+6
View File
@@ -3,3 +3,9 @@ import path from "path";
// given two paths irrespective of ending with / or not // given two paths irrespective of ending with / or not
// this will return true if its equal // this will return true if its equal
export const isSamePath = async (from: string, to: string) => !path.relative(from, to); export const isSamePath = async (from: string, to: string) => !path.relative(from, to);
export const removeTrailingSlash = (str: string) => {
if (str === "/") return str;
return str.endsWith("/") ? str.slice(0, -1) : str;
};
+3 -3
View File
@@ -15,15 +15,15 @@ export const fastifyErrHandler = fastifyPlugin(async (server: FastifyZodProvider
req.log.error(error); req.log.error(error);
if (error instanceof BadRequestError) { if (error instanceof BadRequestError) {
res.status(400).send({ statusCode: 400, message: error.message, error: error.name }); res.status(400).send({ statusCode: 400, message: error.message, error: error.name });
} else if (error instanceof UnauthorizedError || error instanceof ForbiddenRequestError) { } else if (error instanceof UnauthorizedError) {
res.status(403).send({ statusCode: 403, message: error.message, error: error.name }); res.status(403).send({ statusCode: 403, message: error.message, error: error.name });
} else if (error instanceof DatabaseError || error instanceof InternalServerError) { } else if (error instanceof DatabaseError || error instanceof InternalServerError) {
res.status(500).send({ statusCode: 500, message: "Something went wrong", error: error.name }); res.status(500).send({ statusCode: 500, message: "Something went wrong", error: error.name });
} else if (error instanceof ZodError) { } else if (error instanceof ZodError) {
res.status(403).send({ statusCode: 403, error: "ValidationFailure", message: error.issues }); res.status(403).send({ statusCode: 403, error: "ValidationFailure", message: error.issues });
} else if (error instanceof ForbiddenError) { } else if (error instanceof ForbiddenError) {
res.status(403).send({ res.status(401).send({
statusCode: 403, statusCode: 401,
error: "PermissionDenied", error: "PermissionDenied",
message: `You are not allowed to ${error.action} on ${error.subjectType}` message: `You are not allowed to ${error.action} on ${error.subjectType}`
}); });
@@ -2,7 +2,7 @@ import { z } from "zod";
import { IntegrationsSchema } from "@app/db/schemas"; import { IntegrationsSchema } from "@app/db/schemas";
import { EventType } from "@app/ee/services/audit-log/audit-log-types"; import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { shake } from "@app/lib/fn"; import { removeTrailingSlash, shake } from "@app/lib/fn";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type"; import { AuthMode } from "@app/services/auth/auth-type";
@@ -16,7 +16,7 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => {
app: z.string().trim().optional(), app: z.string().trim().optional(),
isActive: z.boolean(), isActive: z.boolean(),
appId: z.string().trim().optional(), appId: z.string().trim().optional(),
secretPath: z.string().trim().default("/"), secretPath: z.string().trim().default("/").transform(removeTrailingSlash),
sourceEnvironment: z.string().trim(), sourceEnvironment: z.string().trim(),
targetEnvironment: z.string().trim().optional(), targetEnvironment: z.string().trim().optional(),
targetEnvironmentId: z.string().trim().optional(), targetEnvironmentId: z.string().trim().optional(),
@@ -89,7 +89,7 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => {
app: z.string().trim(), app: z.string().trim(),
appId: z.string().trim(), appId: z.string().trim(),
isActive: z.boolean(), isActive: z.boolean(),
secretPath: z.string().trim().default("/"), secretPath: z.string().trim().default("/").transform(removeTrailingSlash),
targetEnvironment: z.string().trim(), targetEnvironment: z.string().trim(),
owner: z.string().trim(), owner: z.string().trim(),
environment: z.string().trim() environment: z.string().trim()
@@ -2,6 +2,7 @@ import { z } from "zod";
import { SecretFoldersSchema } from "@app/db/schemas"; import { SecretFoldersSchema } from "@app/db/schemas";
import { EventType } from "@app/ee/services/audit-log/audit-log-types"; import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { removeTrailingSlash } from "@app/lib/fn";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type"; import { AuthMode } from "@app/services/auth/auth-type";
@@ -14,9 +15,9 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
workspaceId: z.string().trim(), workspaceId: z.string().trim(),
environment: z.string().trim(), environment: z.string().trim(),
name: z.string().trim(), name: z.string().trim(),
path: z.string().trim().default("/"), path: z.string().trim().default("/").transform(removeTrailingSlash),
// backward compatiability with cli // backward compatiability with cli
directory: z.string().trim().default("/") directory: z.string().trim().default("/").transform(removeTrailingSlash)
}), }),
response: { response: {
200: z.object({ 200: z.object({
@@ -68,9 +69,9 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
workspaceId: z.string().trim(), workspaceId: z.string().trim(),
environment: z.string().trim(), environment: z.string().trim(),
name: z.string().trim(), name: z.string().trim(),
path: z.string().trim().default("/"), path: z.string().trim().default("/").transform(removeTrailingSlash),
// backward compatiability with cli // backward compatiability with cli
directory: z.string().trim().default("/") directory: z.string().trim().default("/").transform(removeTrailingSlash)
}), }),
response: { response: {
200: z.object({ 200: z.object({
@@ -122,9 +123,9 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
body: z.object({ body: z.object({
workspaceId: z.string().trim(), workspaceId: z.string().trim(),
environment: z.string().trim(), environment: z.string().trim(),
path: z.string().trim().default("/"), path: z.string().trim().default("/").transform(removeTrailingSlash),
// keep this here as cli need directory // keep this here as cli need directory
directory: z.string().trim().default("/") directory: z.string().trim().default("/").transform(removeTrailingSlash)
}), }),
response: { response: {
200: z.object({ 200: z.object({
@@ -172,9 +173,9 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
querystring: z.object({ querystring: z.object({
workspaceId: z.string().trim(), workspaceId: z.string().trim(),
environment: z.string().trim(), environment: z.string().trim(),
path: z.string().trim().default("/"), path: z.string().trim().default("/").transform(removeTrailingSlash),
// backward compatiability with cli // backward compatiability with cli
directory: z.string().trim().default("/") directory: z.string().trim().default("/").transform(removeTrailingSlash)
}), }),
response: { response: {
200: z.object({ 200: z.object({
@@ -2,6 +2,7 @@ import { z } from "zod";
import { SecretImportsSchema, SecretsSchema } from "@app/db/schemas"; import { SecretImportsSchema, SecretsSchema } from "@app/db/schemas";
import { EventType } from "@app/ee/services/audit-log/audit-log-types"; import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { removeTrailingSlash } from "@app/lib/fn";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type"; import { AuthMode } from "@app/services/auth/auth-type";
@@ -13,10 +14,10 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
body: z.object({ body: z.object({
workspaceId: z.string().trim(), workspaceId: z.string().trim(),
environment: z.string().trim(), environment: z.string().trim(),
path: z.string().trim().default("/"), path: z.string().trim().default("/").transform(removeTrailingSlash),
import: z.object({ import: z.object({
environment: z.string().trim(), environment: z.string().trim(),
path: z.string().trim() path: z.string().trim().transform(removeTrailingSlash)
}) })
}), }),
response: { response: {
@@ -74,10 +75,14 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
body: z.object({ body: z.object({
workspaceId: z.string().trim(), workspaceId: z.string().trim(),
environment: z.string().trim(), environment: z.string().trim(),
path: z.string().trim().default("/"), path: z.string().trim().default("/").transform(removeTrailingSlash),
import: z.object({ import: z.object({
environment: z.string().trim().optional(), environment: z.string().trim().optional(),
path: z.string().trim().optional(), path: z
.string()
.trim()
.optional()
.transform((val) => (val ? removeTrailingSlash(val) : val)),
position: z.number().optional() position: z.number().optional()
}) })
}), }),
@@ -137,7 +142,7 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
body: z.object({ body: z.object({
workspaceId: z.string().trim(), workspaceId: z.string().trim(),
environment: z.string().trim(), environment: z.string().trim(),
path: z.string().trim().default("/") path: z.string().trim().default("/").transform(removeTrailingSlash)
}), }),
response: { response: {
200: z.object({ 200: z.object({
@@ -191,7 +196,7 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
querystring: z.object({ querystring: z.object({
workspaceId: z.string().trim(), workspaceId: z.string().trim(),
environment: z.string().trim(), environment: z.string().trim(),
path: z.string().trim().default("/") path: z.string().trim().default("/").transform(removeTrailingSlash)
}), }),
response: { response: {
200: z.object({ 200: z.object({
@@ -243,7 +248,7 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
querystring: z.object({ querystring: z.object({
workspaceId: z.string().trim(), workspaceId: z.string().trim(),
environment: z.string().trim(), environment: z.string().trim(),
path: z.string().trim().default("/") path: z.string().trim().default("/").transform(removeTrailingSlash)
}), }),
response: { response: {
200: z.object({ 200: z.object({
@@ -2,6 +2,7 @@ import { z } from "zod";
import { WebhooksSchema } from "@app/db/schemas"; import { WebhooksSchema } from "@app/db/schemas";
import { EventType } from "@app/ee/services/audit-log/audit-log-types"; import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { removeTrailingSlash } from "@app/lib/fn";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type"; import { AuthMode } from "@app/services/auth/auth-type";
@@ -33,7 +34,7 @@ export const registerWebhookRouter = async (server: FastifyZodProvider) => {
environment: z.string().trim(), environment: z.string().trim(),
webhookUrl: z.string().url().trim(), webhookUrl: z.string().url().trim(),
webhookSecretKey: z.string().trim().optional(), webhookSecretKey: z.string().trim().optional(),
secretPath: z.string().trim().default("/") secretPath: z.string().trim().default("/").transform(removeTrailingSlash)
}), }),
response: { response: {
200: z.object({ 200: z.object({
@@ -182,7 +183,7 @@ export const registerWebhookRouter = async (server: FastifyZodProvider) => {
querystring: z.object({ querystring: z.object({
workspaceId: z.string().trim(), workspaceId: z.string().trim(),
environment: z.string().trim().optional(), environment: z.string().trim().optional(),
secretPath: z.string().trim().optional() secretPath: z.string().trim().optional().transform((val)=> val?removeTrailingSlash(val):val)
}), }),
response: { response: {
200: z.object({ 200: z.object({
@@ -2,6 +2,7 @@ import { z } from "zod";
import { ServiceTokensSchema } from "@app/db/schemas"; import { ServiceTokensSchema } from "@app/db/schemas";
import { EventType } from "@app/ee/services/audit-log/audit-log-types"; import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { removeTrailingSlash } from "@app/lib/fn";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type"; import { AuthMode } from "@app/services/auth/auth-type";
@@ -42,7 +43,7 @@ export const registerServiceTokenRouter = async (server: FastifyZodProvider) =>
scopes: z scopes: z
.object({ .object({
environment: z.string().trim(), environment: z.string().trim(),
secretPath: z.string().trim() secretPath: z.string().trim().transform(removeTrailingSlash)
}) })
.array() .array()
.min(1), .min(1),
+14 -13
View File
@@ -12,6 +12,7 @@ import {
import { EventType } from "@app/ee/services/audit-log/audit-log-types"; import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { CommitType } from "@app/ee/services/secret-approval-request/secret-approval-request-types"; import { CommitType } from "@app/ee/services/secret-approval-request/secret-approval-request-types";
import { BadRequestError } from "@app/lib/errors"; import { BadRequestError } from "@app/lib/errors";
import { removeTrailingSlash } from "@app/lib/fn";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { ActorType, AuthMode } from "@app/services/auth/auth-type"; import { ActorType, AuthMode } from "@app/services/auth/auth-type";
import { PostHogEventTypes } from "@app/services/telemetry/telemetry-types"; import { PostHogEventTypes } from "@app/services/telemetry/telemetry-types";
@@ -39,7 +40,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
querystring: z.object({ querystring: z.object({
workspaceId: z.string().trim().optional(), workspaceId: z.string().trim().optional(),
environment: z.string().trim().optional(), environment: z.string().trim().optional(),
secretPath: z.string().trim().default("/"), secretPath: z.string().trim().default("/").transform(removeTrailingSlash),
include_imports: z include_imports: z
.enum(["true", "false"]) .enum(["true", "false"])
.default("false") .default("false")
@@ -129,7 +130,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
querystring: z.object({ querystring: z.object({
workspaceId: z.string().trim().optional(), workspaceId: z.string().trim().optional(),
environment: z.string().trim().optional(), environment: z.string().trim().optional(),
secretPath: z.string().trim().default("/"), secretPath: z.string().trim().default("/").transform(removeTrailingSlash),
version: z.coerce.number().optional(), version: z.coerce.number().optional(),
type: z.nativeEnum(SecretType).default(SecretType.Shared), type: z.nativeEnum(SecretType).default(SecretType.Shared),
include_imports: z include_imports: z
@@ -216,7 +217,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
body: z.object({ body: z.object({
workspaceId: z.string().trim(), workspaceId: z.string().trim(),
environment: z.string().trim(), environment: z.string().trim(),
secretPath: z.string().trim().default("/"), secretPath: z.string().trim().default("/").transform(removeTrailingSlash),
secretValue: z secretValue: z
.string() .string()
.transform((val) => (val.at(-1) === "\n" ? `${val.trim()}\n` : val.trim())), .transform((val) => (val.at(-1) === "\n" ? `${val.trim()}\n` : val.trim())),
@@ -295,7 +296,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
secretValue: z secretValue: z
.string() .string()
.transform((val) => (val.at(-1) === "\n" ? `${val.trim()}\n` : val.trim())), .transform((val) => (val.at(-1) === "\n" ? `${val.trim()}\n` : val.trim())),
secretPath: z.string().trim().default("/"), secretPath: z.string().trim().default("/").transform(removeTrailingSlash),
skipMultilineEncoding: z.boolean().optional(), skipMultilineEncoding: z.boolean().optional(),
type: z.nativeEnum(SecretType).default(SecretType.Shared) type: z.nativeEnum(SecretType).default(SecretType.Shared)
}), }),
@@ -365,7 +366,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
body: z.object({ body: z.object({
workspaceId: z.string().trim(), workspaceId: z.string().trim(),
environment: z.string().trim(), environment: z.string().trim(),
secretPath: z.string().trim().default("/"), secretPath: z.string().trim().default("/").transform(removeTrailingSlash),
type: z.nativeEnum(SecretType).default(SecretType.Shared) type: z.nativeEnum(SecretType).default(SecretType.Shared)
}), }),
response: { response: {
@@ -430,7 +431,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
querystring: z.object({ querystring: z.object({
workspaceId: z.string().trim(), workspaceId: z.string().trim(),
environment: z.string().trim(), environment: z.string().trim(),
secretPath: z.string().trim().default("/"), secretPath: z.string().trim().default("/").transform(removeTrailingSlash),
include_imports: z include_imports: z
.enum(["true", "false"]) .enum(["true", "false"])
.default("false") .default("false")
@@ -518,7 +519,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
querystring: z.object({ querystring: z.object({
workspaceId: z.string().trim(), workspaceId: z.string().trim(),
environment: z.string().trim(), environment: z.string().trim(),
secretPath: z.string().trim().default("/"), secretPath: z.string().trim().default("/").transform(removeTrailingSlash),
type: z.nativeEnum(SecretType).default(SecretType.Shared), type: z.nativeEnum(SecretType).default(SecretType.Shared),
version: z.coerce.number().optional(), version: z.coerce.number().optional(),
include_imports: z include_imports: z
@@ -590,7 +591,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
workspaceId: z.string().trim(), workspaceId: z.string().trim(),
environment: z.string().trim(), environment: z.string().trim(),
type: z.nativeEnum(SecretType).default(SecretType.Shared), type: z.nativeEnum(SecretType).default(SecretType.Shared),
secretPath: z.string().trim().default("/"), secretPath: z.string().trim().default("/").transform(removeTrailingSlash),
secretKeyCiphertext: z.string().trim(), secretKeyCiphertext: z.string().trim(),
secretKeyIV: z.string().trim(), secretKeyIV: z.string().trim(),
secretKeyTag: z.string().trim(), secretKeyTag: z.string().trim(),
@@ -758,7 +759,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
environment: z.string().trim(), environment: z.string().trim(),
secretId: z.string().trim().optional(), secretId: z.string().trim().optional(),
type: z.nativeEnum(SecretType).default(SecretType.Shared), type: z.nativeEnum(SecretType).default(SecretType.Shared),
secretPath: z.string().trim().default("/"), secretPath: z.string().trim().default("/").transform(removeTrailingSlash),
secretValueCiphertext: z.string().trim(), secretValueCiphertext: z.string().trim(),
secretValueIV: z.string().trim(), secretValueIV: z.string().trim(),
secretValueTag: z.string().trim(), secretValueTag: z.string().trim(),
@@ -935,7 +936,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
}), }),
body: z.object({ body: z.object({
type: z.nativeEnum(SecretType).default(SecretType.Shared), type: z.nativeEnum(SecretType).default(SecretType.Shared),
secretPath: z.string().trim().default("/"), secretPath: z.string().trim().default("/").transform(removeTrailingSlash),
secretId: z.string().trim().optional(), secretId: z.string().trim().optional(),
workspaceId: z.string().trim(), workspaceId: z.string().trim(),
environment: z.string().trim() environment: z.string().trim()
@@ -1050,7 +1051,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
body: z.object({ body: z.object({
workspaceId: z.string().trim(), workspaceId: z.string().trim(),
environment: z.string().trim(), environment: z.string().trim(),
secretPath: z.string().trim().default("/"), secretPath: z.string().trim().default("/").transform(removeTrailingSlash),
secrets: z secrets: z
.object({ .object({
secretName: z.string().trim(), secretName: z.string().trim(),
@@ -1176,7 +1177,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
body: z.object({ body: z.object({
workspaceId: z.string().trim(), workspaceId: z.string().trim(),
environment: z.string().trim(), environment: z.string().trim(),
secretPath: z.string().trim().default("/"), secretPath: z.string().trim().default("/").transform(removeTrailingSlash),
secrets: z secrets: z
.object({ .object({
secretName: z.string().trim(), secretName: z.string().trim(),
@@ -1301,7 +1302,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
body: z.object({ body: z.object({
workspaceId: z.string().trim(), workspaceId: z.string().trim(),
environment: z.string().trim(), environment: z.string().trim(),
secretPath: z.string().trim().default("/"), secretPath: z.string().trim().default("/").transform(removeTrailingSlash),
secrets: z secrets: z
.object({ .object({
secretName: z.string().trim(), secretName: z.string().trim(),
@@ -29,10 +29,7 @@ export const secretBlindIndexServiceFactory = ({
projectId, projectId,
actorId actorId
}: TGetProjectBlindIndexStatusDTO) => { }: TGetProjectBlindIndexStatusDTO) => {
const { membership } = await permissionService.getProjectPermission(actor, actorId, projectId); await permissionService.getProjectPermission(actor, actorId, projectId);
if (membership?.role !== ProjectMembershipRole.Admin) {
throw new UnauthorizedError({ message: "User must be admin" });
}
const secretCount = await secretBlindIndexDAL.countOfSecretsWithNullSecretBlindIndex(projectId); const secretCount = await secretBlindIndexDAL.countOfSecretsWithNullSecretBlindIndex(projectId);
return Number(secretCount); return Number(secretCount);
@@ -8,7 +8,7 @@ import {
TSecretFoldersUpdate TSecretFoldersUpdate
} from "@app/db/schemas"; } from "@app/db/schemas";
import { BadRequestError, DatabaseError } from "@app/lib/errors"; import { BadRequestError, DatabaseError } from "@app/lib/errors";
import { groupBy } from "@app/lib/fn"; import { groupBy, removeTrailingSlash } from "@app/lib/fn";
import { ormify, selectAllTableCols } from "@app/lib/knex"; import { ormify, selectAllTableCols } from "@app/lib/knex";
export const validateFolderName = (folderName: string) => { export const validateFolderName = (folderName: string) => {
@@ -238,10 +238,15 @@ export const secretFolderDALFactory = (db: TDbClient) => {
tx?: Knex tx?: Knex
) => { ) => {
try { try {
const folder = await sqlFindFolderByPathQuery(tx || db, projectId, environment, path) const folder = await sqlFindFolderByPathQuery(
tx || db,
projectId,
environment,
removeTrailingSlash(path)
)
.orderBy("depth", "desc") .orderBy("depth", "desc")
.first(); .first();
if (folder && folder.path !== path) { if (folder && folder.path !== removeTrailingSlash(path)) {
return; return;
} }
if (!folder) return; if (!folder) return;
@@ -262,7 +267,12 @@ export const secretFolderDALFactory = (db: TDbClient) => {
tx?: Knex tx?: Knex
) => { ) => {
try { try {
const folder = await sqlFindFolderByPathQuery(tx || db, projectId, environment, path) const folder = await sqlFindFolderByPathQuery(
tx || db,
projectId,
environment,
removeTrailingSlash(path)
)
.orderBy("depth", "desc") .orderBy("depth", "desc")
.first(); .first();
if (!folder) return; if (!folder) return;
@@ -278,8 +288,12 @@ export const secretFolderDALFactory = (db: TDbClient) => {
tx?: Knex tx?: Knex
) => { ) => {
try { try {
const folders = await sqlFindMultipleFolderByEnvPathQuery(tx || db, query); const formatedQuery = query.map(({ secretPath, envId }) => ({
return query.map(({ envId, secretPath }) => envId,
secretPath: removeTrailingSlash(secretPath)
}));
const folders = await sqlFindMultipleFolderByEnvPathQuery(tx || db, formatedQuery);
return formatedQuery.map(({ envId, secretPath }) =>
folders.find( folders.find(
({ path: targetPath, envId: targetEnvId }) => ({ path: targetPath, envId: targetEnvId }) =>
targetPath === secretPath && targetEnvId === envId targetPath === secretPath && targetEnvId === envId
@@ -25,7 +25,7 @@ export const ProjectPermissionCan: FunctionComponent<Props> = ({
allowedLabel, allowedLabel,
...props ...props
}) => { }) => {
const permission = useProjectPermission(); const { permission } = useProjectPermission();
return ( return (
<Can {...props} passThrough={passThrough} ability={props?.ability || permission}> <Can {...props} passThrough={passThrough} ability={props?.ability || permission}>
{(isAllowed, ability) => { {(isAllowed, ability) => {
@@ -1,6 +1,7 @@
import { createContext, ReactNode, useContext } from "react"; import { createContext, ReactNode, useContext } from "react";
import { useGetUserProjectPermissions } from "@app/hooks/api"; import { useGetUserProjectPermissions } from "@app/hooks/api";
import { TProjectMembership } from "@app/hooks/api/users/types";
import { useWorkspace } from "../WorkspaceContext"; import { useWorkspace } from "../WorkspaceContext";
import { TProjectPermission } from "./types"; import { TProjectPermission } from "./types";
@@ -9,7 +10,10 @@ type Props = {
children: ReactNode; children: ReactNode;
}; };
const ProjectPermissionContext = createContext<null | TProjectPermission>(null); const ProjectPermissionContext = createContext<null | {
permission: TProjectPermission;
membership: TProjectMembership;
}>(null);
export const ProjectPermissionProvider = ({ children }: Props): JSX.Element => { export const ProjectPermissionProvider = ({ children }: Props): JSX.Element => {
const { currentWorkspace, isLoading: isWsLoading } = useWorkspace(); const { currentWorkspace, isLoading: isWsLoading } = useWorkspace();
@@ -21,7 +21,7 @@ export const withProjectPermission = <T extends {}, J extends TProjectPermission
{ action, subject, className, containerClassName }: Props<Generics<J>["abilities"]> { action, subject, className, containerClassName }: Props<Generics<J>["abilities"]>
) => { ) => {
const HOC = (hocProps: T) => { const HOC = (hocProps: T) => {
const permission = useProjectPermission(); const { permission } = useProjectPermission();
// akhilmhdh: Set as any due to casl/react ts type bug // akhilmhdh: Set as any due to casl/react ts type bug
// REASON: casl due to its type checking can't seem to union even if union intersection is applied // REASON: casl due to its type checking can't seem to union even if union intersection is applied
@@ -29,13 +29,13 @@ export const withProjectPermission = <T extends {}, J extends TProjectPermission
return ( return (
<div <div
className={twMerge( className={twMerge(
"container h-full mx-auto flex justify-center items-center", "container mx-auto flex h-full items-center justify-center",
containerClassName containerClassName
)} )}
> >
<div <div
className={twMerge( className={twMerge(
"rounded-md bg-mineshaft-800 text-bunker-300 p-16 flex space-x-12 items-end", "flex items-end space-x-12 rounded-md bg-mineshaft-800 p-16 text-bunker-300",
className className
)} )}
> >
@@ -43,7 +43,7 @@ export const withProjectPermission = <T extends {}, J extends TProjectPermission
<FontAwesomeIcon icon={faLock} size="6x" /> <FontAwesomeIcon icon={faLock} size="6x" />
</div> </div>
<div> <div>
<div className="text-4xl font-medium mb-2">Permission Denied</div> <div className="mb-2 text-4xl font-medium">Permission Denied</div>
<div className="text-sm"> <div className="text-sm">
You do not have permission to this page. <br /> Kindly contact your organization You do not have permission to this page. <br /> Kindly contact your organization
administrator administrator
+8 -5
View File
@@ -8,7 +8,7 @@ import { apiRequest } from "@app/config/request";
import { OrgPermissionSet } from "@app/context/OrgPermissionContext/types"; import { OrgPermissionSet } from "@app/context/OrgPermissionContext/types";
import { ProjectPermissionSet } from "@app/context/ProjectPermissionContext/types"; import { ProjectPermissionSet } from "@app/context/ProjectPermissionContext/types";
import { OrgUser } from "../users/types"; import { OrgUser, TProjectMembership } from "../users/types";
import { import {
TGetUserOrgPermissionsDTO, TGetUserOrgPermissionsDTO,
TGetUserProjectPermissionDTO, TGetUserProjectPermissionDTO,
@@ -104,10 +104,13 @@ export const useGetUserOrgPermissions = ({ orgId }: TGetUserOrgPermissionsDTO) =
const getUserProjectPermissions = async ({ workspaceId }: TGetUserProjectPermissionDTO) => { const getUserProjectPermissions = async ({ workspaceId }: TGetUserProjectPermissionDTO) => {
const { data } = await apiRequest.get<{ const { data } = await apiRequest.get<{
data: { permissions: PackRule<RawRuleOf<MongoAbility<OrgPermissionSet>>>[] }; data: {
permissions: PackRule<RawRuleOf<MongoAbility<OrgPermissionSet>>>[];
membership: TProjectMembership;
};
}>(`/api/v1/workspace/${workspaceId}/permissions`, {}); }>(`/api/v1/workspace/${workspaceId}/permissions`, {});
return data.data.permissions; return data.data;
}; };
export const useGetUserProjectPermissions = ({ workspaceId }: TGetUserProjectPermissionDTO) => export const useGetUserProjectPermissions = ({ workspaceId }: TGetUserProjectPermissionDTO) =>
@@ -116,8 +119,8 @@ export const useGetUserProjectPermissions = ({ workspaceId }: TGetUserProjectPer
queryFn: () => getUserProjectPermissions({ workspaceId }), queryFn: () => getUserProjectPermissions({ workspaceId }),
enabled: Boolean(workspaceId), enabled: Boolean(workspaceId),
select: (data) => { select: (data) => {
const rule = unpackRules<RawRuleOf<MongoAbility<ProjectPermissionSet>>>(data); const rule = unpackRules<RawRuleOf<MongoAbility<ProjectPermissionSet>>>(data.permissions);
const ability = createMongoAbility<ProjectPermissionSet>(rule, { conditionsMatcher }); const ability = createMongoAbility<ProjectPermissionSet>(rule, { conditionsMatcher });
return ability; return { permission: ability, membership: data.membership };
} }
}); });
+9
View File
@@ -52,6 +52,15 @@ export type OrgUser = {
roleId: string; roleId: string;
}; };
export type TProjectMembership = {
id: string;
role: string;
createdAt: string;
updatedAt: string;
projectId: string;
roleId: string;
};
export type TWorkspaceUser = OrgUser; export type TWorkspaceUser = OrgUser;
export type AddUserToWsDTO = { export type AddUserToWsDTO = {
@@ -30,7 +30,7 @@ export const CloudIntegrationSection = ({
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([ const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([
"deleteConfirmation" "deleteConfirmation"
] as const); ] as const);
const permission = useProjectPermission(); const { permission } = useProjectPermission();
const { createNotification } = useNotificationContext(); const { createNotification } = useNotificationContext();
const isEmpty = !isLoading && !cloudIntegrations?.length; const isEmpty = !isLoading && !cloudIntegrations?.length;
@@ -43,7 +43,7 @@ export const CloudIntegrationSection = ({
<h1 className="text-3xl font-semibold">{t("integrations.cloud-integrations")}</h1> <h1 className="text-3xl font-semibold">{t("integrations.cloud-integrations")}</h1>
<p className="text-base text-gray-400">{t("integrations.click-to-start")}</p> <p className="text-base text-gray-400">{t("integrations.click-to-start")}</p>
</div> </div>
<div className="mx-6 grid grid-cols-2 lg:grid-cols-3 2xl:grid-cols-4 gap-4"> <div className="mx-6 grid grid-cols-2 gap-4 lg:grid-cols-3 2xl:grid-cols-4">
{isLoading && {isLoading &&
Array.from({ length: 12 }).map((_, index) => ( Array.from({ length: 12 }).map((_, index) => (
<Skeleton className="h-32" key={`cloud-integration-skeleton-${index + 1}`} /> <Skeleton className="h-32" key={`cloud-integration-skeleton-${index + 1}`} />
@@ -44,7 +44,7 @@ export const SecretApprovalPolicyList = ({ workspaceId }: Props) => {
"deletePolicy", "deletePolicy",
"upgradePlan" "upgradePlan"
] as const); ] as const);
const permission = useProjectPermission(); const { permission } = useProjectPermission();
const { subscription } = useSubscription(); const { subscription } = useSubscription();
const { createNotification } = useNotificationContext(); const { createNotification } = useNotificationContext();
@@ -36,7 +36,7 @@ export const SecretApprovalPolicyRow = ({
}: Props) => { }: Props) => {
const [selectedApprovers, setSelectedApprovers] = useState<string[]>([]); const [selectedApprovers, setSelectedApprovers] = useState<string[]>([]);
const { mutate: updateSecretApprovalPolicy, isLoading } = useUpdateSecretApprovalPolicy(); const { mutate: updateSecretApprovalPolicy, isLoading } = useUpdateSecretApprovalPolicy();
const permission = useProjectPermission(); const { permission } = useProjectPermission();
return ( return (
<Tr> <Tr>
@@ -48,7 +48,7 @@ export const SecretMainPage = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const { currentWorkspace } = useWorkspace(); const { currentWorkspace } = useWorkspace();
const router = useRouter(); const router = useRouter();
const permission = useProjectPermission(); const { permission } = useProjectPermission();
const [isVisible, setIsVisible] = useState(false); const [isVisible, setIsVisible] = useState(false);
const [sortDir, setSortDir] = useState<SortDir>(SortDir.ASC); const [sortDir, setSortDir] = useState<SortDir>(SortDir.ASC);
@@ -81,7 +81,7 @@ export const SecretDetailSidebar = ({
resolver: zodResolver(formSchema), resolver: zodResolver(formSchema),
values: secret values: secret
}); });
const permission = useProjectPermission(); const { permission } = useProjectPermission();
const cannotEditSecret = permission.cannot( const cannotEditSecret = permission.cannot(
ProjectPermissionActions.Edit, ProjectPermissionActions.Edit,
subject(ProjectPermissionSub.Secrets, { environment, secretPath }) subject(ProjectPermissionSub.Secrets, { environment, secretPath })
@@ -85,7 +85,7 @@ export const SecretItem = memo(
secretPath secretPath
}: Props) => { }: Props) => {
const { currentWorkspace } = useWorkspace(); const { currentWorkspace } = useWorkspace();
const permission = useProjectPermission(); const { permission } = useProjectPermission();
const isReadOnly = const isReadOnly =
permission.can( permission.can(
ProjectPermissionActions.Read, ProjectPermissionActions.Read,
@@ -4,9 +4,15 @@ import {
decryptSymmetric decryptSymmetric
} from "@app/components/utilities/cryptography/crypto"; } from "@app/components/utilities/cryptography/crypto";
import { Button, Spinner } from "@app/components/v2"; import { Button, Spinner } from "@app/components/v2";
import { ProjectPermissionActions, ProjectPermissionSub, useWorkspace } from "@app/context"; import {
ProjectPermissionActions,
ProjectPermissionSub,
useProjectPermission,
useWorkspace
} from "@app/context";
import { useToggle } from "@app/hooks"; import { useToggle } from "@app/hooks";
import { useGetWorkspaceIndexStatus, useNameWorkspaceSecrets } from "@app/hooks/api"; import { useGetWorkspaceIndexStatus, useNameWorkspaceSecrets } from "@app/hooks/api";
import { ProjectMembershipRole } from "@app/hooks/api/roles/types";
import { UserWsKeyPair } from "@app/hooks/api/types"; import { UserWsKeyPair } from "@app/hooks/api/types";
import { fetchWorkspaceSecrets } from "@app/hooks/api/workspace/queries"; import { fetchWorkspaceSecrets } from "@app/hooks/api/workspace/queries";
@@ -18,6 +24,7 @@ type Props = {
export const ProjectIndexSecretsSection = ({ decryptFileKey }: Props) => { export const ProjectIndexSecretsSection = ({ decryptFileKey }: Props) => {
const { currentWorkspace } = useWorkspace(); const { currentWorkspace } = useWorkspace();
const { membership } = useProjectPermission();
const { data: isBlindIndexed, isLoading: isBlindIndexedLoading } = useGetWorkspaceIndexStatus( const { data: isBlindIndexed, isLoading: isBlindIndexedLoading } = useGetWorkspaceIndexStatus(
currentWorkspace?.id ?? "" currentWorkspace?.id ?? ""
); );
@@ -74,17 +81,19 @@ export const ProjectIndexSecretsSection = ({ decryptFileKey }: Props) => {
</div> </div>
</div> </div>
)} )}
<p className="mb-2 text-lg font-semibold">Enable Blind Indices</p> <p className="mb-2 text-lg font-semibold">Action Required</p>
<p className="mb-4 leading-7 text-gray-400"> <p className="mb-4 leading-7 text-gray-400">
Your project was created before the introduction of blind indexing. To continue accessing Your project was created before the introduction of blind indexing. To continue accessing
secrets by name through the SDK, public API and web dashboard, please enable blind indexing.{" "} secrets by name through the SDK, public API and web dashboard, please enable blind indexing.{" "}
<b>This is a one time process.</b> <b>
{membership.role !== ProjectMembershipRole.Admin && "This is an admin only operation."}
</b>
</p> </p>
<ProjectPermissionCan I={ProjectPermissionActions.Edit} a={ProjectPermissionSub.Settings}> <ProjectPermissionCan I={ProjectPermissionActions.Edit} a={ProjectPermissionSub.Settings}>
{(isAllowed) => ( {(isAllowed) => (
<Button <Button
onClick={onEnableBlindIndices} onClick={onEnableBlindIndices}
isDisabled={!isAllowed} isDisabled={!isAllowed || membership.role !== ProjectMembershipRole.Admin}
color="mineshaft" color="mineshaft"
type="submit" type="submit"
isLoading={isIndexing} isLoading={isIndexing}
@@ -62,7 +62,7 @@ export const SecretRotationPage = withProjectPermission(
() => { () => {
const { currentWorkspace } = useWorkspace(); const { currentWorkspace } = useWorkspace();
const { t } = useTranslation(); const { t } = useTranslation();
const permission = useProjectPermission(); const { permission } = useProjectPermission();
const { createNotification } = useNotificationContext(); const { createNotification } = useNotificationContext();
const { popUp, handlePopUpOpen, handlePopUpToggle, handlePopUpClose } = usePopUp([ const { popUp, handlePopUpOpen, handlePopUpToggle, handlePopUpClose } = usePopUp([
"createRotation", "createRotation",
@@ -22,7 +22,7 @@ export const EnvironmentSection = () => {
const { createNotification } = useNotificationContext(); const { createNotification } = useNotificationContext();
const { subscription } = useSubscription(); const { subscription } = useSubscription();
const { currentWorkspace } = useWorkspace(); const { currentWorkspace } = useWorkspace();
const permision = useProjectPermission(); const { permission } = useProjectPermission();
const deleteWsEnvironment = useDeleteWsEnvironment(); const deleteWsEnvironment = useDeleteWsEnvironment();
@@ -94,7 +94,7 @@ export const EnvironmentSection = () => {
Choose which environments will show up in your dashboard like development, staging, Choose which environments will show up in your dashboard like development, staging,
production production
</p> </p>
{permision.can(ProjectPermissionActions.Read, ProjectPermissionSub.Environments) ? ( {permission.can(ProjectPermissionActions.Read, ProjectPermissionSub.Environments) ? (
<EnvironmentTable handlePopUpOpen={handlePopUpOpen} /> <EnvironmentTable handlePopUpOpen={handlePopUpOpen} />
) : ( ) : (
<PermissionDeniedBanner /> <PermissionDeniedBanner />
@@ -4,7 +4,12 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider"; import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
import { PermissionDeniedBanner, ProjectPermissionCan } from "@app/components/permissions"; import { PermissionDeniedBanner, ProjectPermissionCan } from "@app/components/permissions";
import { Button, DeleteActionModal } from "@app/components/v2"; import { Button, DeleteActionModal } from "@app/components/v2";
import { ProjectPermissionActions, ProjectPermissionSub, useProjectPermission, useWorkspace } from "@app/context"; import {
ProjectPermissionActions,
ProjectPermissionSub,
useProjectPermission,
useWorkspace
} from "@app/context";
import { usePopUp } from "@app/hooks"; import { usePopUp } from "@app/hooks";
import { useDeleteWsTag } from "@app/hooks/api"; import { useDeleteWsTag } from "@app/hooks/api";
@@ -20,14 +25,14 @@ export const SecretTagsSection = (): JSX.Element => {
"deleteTagConfirmation" "deleteTagConfirmation"
] as const); ] as const);
const { currentWorkspace } = useWorkspace(); const { currentWorkspace } = useWorkspace();
const permission = useProjectPermission(); const { permission } = useProjectPermission();
const deleteWsTag = useDeleteWsTag(); const deleteWsTag = useDeleteWsTag();
const onDeleteApproved = async () => { const onDeleteApproved = async () => {
try { try {
await deleteWsTag.mutateAsync({ await deleteWsTag.mutateAsync({
projectId:currentWorkspace?.id || "", projectId: currentWorkspace?.id || "",
tagID: (popUp?.deleteTagConfirmation?.data as DeleteModalData)?.id tagID: (popUp?.deleteTagConfirmation?.data as DeleteModalData)?.id
}); });
@@ -47,8 +52,8 @@ export const SecretTagsSection = (): JSX.Element => {
}; };
return ( return (
<div className="mb-6 p-4 bg-mineshaft-900 rounded-lg border border-mineshaft-600"> <div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
<div className="flex justify-between mb-8"> <div className="mb-8 flex justify-between">
<p className="mb-3 text-xl font-semibold">Secret Tags</p> <p className="mb-3 text-xl font-semibold">Secret Tags</p>
<ProjectPermissionCan I={ProjectPermissionActions.Create} a={ProjectPermissionSub.Tags}> <ProjectPermissionCan I={ProjectPermissionActions.Create} a={ProjectPermissionSub.Tags}>
{(isAllowed) => ( {(isAllowed) => (
@@ -65,7 +70,7 @@ export const SecretTagsSection = (): JSX.Element => {
)} )}
</ProjectPermissionCan> </ProjectPermissionCan>
</div> </div>
<p className="text-gray-400 mb-8"> <p className="mb-8 text-gray-400">
Every secret can be assigned to one or more tags. Here you can add and remove tags for the Every secret can be assigned to one or more tags. Here you can add and remove tags for the
current project. current project.
</p> </p>