feat(rbac): added new validation to all routes and permission check to most

This commit is contained in:
Akhil Mohan
2023-09-08 21:22:36 +05:30
parent 520a553ea1
commit 549121f44e
78 changed files with 4518 additions and 3499 deletions
+324 -77
View File
@@ -1,18 +1,15 @@
import { Types } from "mongoose";
import {
ISecret,
IServiceTokenData,
IUser,
Secret,
} from "../models";
import { ISecret, IServiceTokenData, IUser, Secret } from "../models";
import { validateUserClientForSecret, validateUserClientForSecrets } from "./user";
import { validateServiceTokenDataClientForSecrets, validateServiceTokenDataClientForWorkspace } from "./serviceTokenData";
import {
BadRequestError,
SecretNotFoundError,
} from "../utils/errors";
validateServiceTokenDataClientForSecrets,
validateServiceTokenDataClientForWorkspace
} from "./serviceTokenData";
import { BadRequestError, SecretNotFoundError } from "../utils/errors";
import { AuthData } from "../interfaces/middleware";
import { ActorType } from "../ee/models";
import { z } from "zod";
import { SECRET_PERSONAL, SECRET_SHARED } from "../variables";
/**
* Validate authenticated clients for secrets with id [secretId] based
@@ -24,42 +21,43 @@ import { ActorType } from "../ee/models";
* @param {String[]} obj.requiredPermissions - required permissions as part of the endpoint
*/
export const validateClientForSecret = async ({
authData,
secretId,
acceptedRoles,
requiredPermissions,
authData,
secretId,
acceptedRoles,
requiredPermissions
}: {
authData: AuthData;
secretId: Types.ObjectId;
acceptedRoles: Array<"admin" | "member">;
requiredPermissions: string[];
authData: AuthData;
secretId: Types.ObjectId;
acceptedRoles: Array<"admin" | "member">;
requiredPermissions: string[];
}) => {
const secret = await Secret.findById(secretId);
const secret = await Secret.findById(secretId);
if (!secret) throw SecretNotFoundError({
message: "Failed to find secret",
if (!secret)
throw SecretNotFoundError({
message: "Failed to find secret"
});
switch (authData.actor.type) {
case ActorType.USER:
await validateUserClientForSecret({
user: authData.authPayload as IUser,
secret,
acceptedRoles,
requiredPermissions,
});
return secret;
case ActorType.SERVICE:
await validateServiceTokenDataClientForWorkspace({
serviceTokenData: authData.authPayload as IServiceTokenData,
workspaceId: secret.workspace,
environment: secret.environment,
});
return secret;
}
}
switch (authData.actor.type) {
case ActorType.USER:
await validateUserClientForSecret({
user: authData.authPayload as IUser,
secret,
acceptedRoles,
requiredPermissions
});
return secret;
case ActorType.SERVICE:
await validateServiceTokenDataClientForWorkspace({
serviceTokenData: authData.authPayload as IServiceTokenData,
workspaceId: secret.workspace,
environment: secret.environment
});
return secret;
}
};
/**
* Validate authenticated clients for secrets with ids [secretIds] based
@@ -72,43 +70,292 @@ export const validateClientForSecret = async ({
* @param {String[]} obj.requiredPermissions - required permissions as part of the endpoint
*/
export const validateClientForSecrets = async ({
authData,
secretIds,
requiredPermissions,
authData,
secretIds,
requiredPermissions
}: {
authData: AuthData;
secretIds: Types.ObjectId[];
requiredPermissions: string[];
authData: AuthData;
secretIds: Types.ObjectId[];
requiredPermissions: string[];
}) => {
let secrets: ISecret[] = [];
let secrets: ISecret[] = [];
secrets = await Secret.find({
_id: {
$in: secretIds,
},
});
secrets = await Secret.find({
_id: {
$in: secretIds
}
});
if (secrets.length != secretIds.length) {
throw BadRequestError({ message: "Failed to validate non-existent secrets" })
}
switch (authData.actor.type) {
case ActorType.USER:
await validateUserClientForSecrets({
user: authData.authPayload as IUser,
secrets,
requiredPermissions,
});
return secrets;
case ActorType.SERVICE:
await validateServiceTokenDataClientForSecrets({
serviceTokenData: authData.authPayload as IServiceTokenData,
secrets,
requiredPermissions,
});
return secrets;
}
}
if (secrets.length != secretIds.length) {
throw BadRequestError({ message: "Failed to validate non-existent secrets" });
}
switch (authData.actor.type) {
case ActorType.USER:
await validateUserClientForSecrets({
user: authData.authPayload as IUser,
secrets,
requiredPermissions
});
return secrets;
case ActorType.SERVICE:
await validateServiceTokenDataClientForSecrets({
serviceTokenData: authData.authPayload as IServiceTokenData,
secrets,
requiredPermissions
});
return secrets;
}
};
export const GetSecretVersionsV1 = z.object({
params: z.object({
secretId: z.string().trim()
}),
query: z.object({
offset: z.number(),
limit: z.number()
})
});
export const RollbackSecretVersionV1 = z.object({
params: z.object({
secretId: z.string().trim()
}),
body: z.object({
version: z.number()
})
});
export const PushSecretsV1 = z.object({
params: z.object({
workspaceId: z.string().trim()
}),
body: z.object({
secrets: z.object({}).array(),
keys: z.object({}).array(),
environment: z.string().trim(),
channel: z.string().trim()
})
});
export const PullSecretsV1 = z.object({
params: z.object({
workspaceId: z.string().trim()
}),
query: z.object({
channel: z.string().optional(),
environment: z.string().trim()
})
});
export const PullSecretsServiceTokenV1 = z.object({
params: z.object({
workspaceId: z.string().trim()
}),
query: z.object({
channel: z.string().optional(),
environment: z.string().trim()
})
});
const batchUpdateRequestV2 = z.object({
_id: z.string(),
folderId: z.string().trim().optional(),
type: z.enum(["shared", "personal"]),
secretName: z.string().trim(),
secretKeyCiphertext: z.string().trim(),
secretKeyIV: z.string().trim(),
secretKeyTag: z.string().trim(),
secretValueCiphertext: z.string().trim(),
secretValueIV: z.string().trim(),
secretValueTag: z.string().trim(),
secretCommentCiphertext: z.string().trim().optional(),
secretCommentIV: z.string().trim().optional(),
secretCommentTag: z.string().trim().optional(),
tags: z.object({
_id: z.string().trim(),
name: z.string().trim(),
slug: z.string().trim()
})
});
export const BatchSecretsV2 = z.object({
body: z.object({
workspaceId: z.string().trim(),
folderId: z.string().trim(),
environment: z.string().trim(),
secretPath: z.string().trim().optional(),
requests: z
.discriminatedUnion("method", [
z.object({
method: z.literal("POST"),
secret: batchUpdateRequestV2.omit({ _id: true })
}),
z.object({
method: z.literal("PATCH"),
secret: batchUpdateRequestV2
}),
z.object({
method: z.literal("DELETE"),
secret: z.object({ _id: z.string().trim(), secretName: z.string().trim() })
})
])
.array()
})
});
export const GetSecretsV2 = z.object({
query: z.object({
workspaceId: z.string().trim(),
environment: z.string().trim(),
tagSlugs: z.string().trim().optional(),
folderId: z.string().trim().default("root"),
secretPath: z.string().trim().optional(),
include_imports: z
.enum(["true", "false"])
.default("false")
.transform((value) => value === "true")
})
});
export const GetSecretsRawV3 = z.object({
query: z.object({
workspaceId: z.string().trim(),
environment: z.string().trim(),
secretPath: z.string().trim().default("/"),
folderId:z.string().trim().optional(),
include_imports: z
.enum(["true", "false"])
.default("false")
.transform((value) => value === "true")
})
});
export const GetSecretByNameRawV3 = z.object({
params: z.object({
secretName: z.string().trim()
}),
query: z.object({
workspaceId: z.string().trim(),
environment: z.string().trim(),
secretPath: z.string().trim().default("/"),
type: z.enum([SECRET_SHARED, SECRET_PERSONAL]).optional()
})
});
export const CreateSecretRawV3 = z.object({
body: z.object({
workspaceId: z.string().trim(),
environment: z.string().trim(),
secretPath: z.string().trim().default("/"),
secretValue: z.string().trim(),
secretComment: z.string().trim(),
type: z.enum([SECRET_SHARED, SECRET_PERSONAL])
}),
params: z.object({
secretName: z.string().trim()
})
});
export const UpdateSecretByNameRawV3 = z.object({
params: z.object({
secretName: z.string().trim()
}),
body: z.object({
workspaceId: z.string().trim(),
environment: z.string().trim(),
secretValue: z.string().trim(),
secretName: z.string().trim(),
secretPath: z.string().trim().default("/"),
type: z.enum([SECRET_SHARED, SECRET_PERSONAL]).default(SECRET_SHARED)
})
});
export const DeleteSecretByNameRawV3 = z.object({
params: z.object({
secretName: z.string().trim()
}),
body: z.object({
workspaceId: z.string().trim(),
environment: z.string().trim(),
secretPath: z.string().trim().default("/"),
type: z.enum([SECRET_SHARED, SECRET_PERSONAL]).default(SECRET_SHARED)
})
});
export const GetSecretsV3 = z.object({
query: z.object({
workspaceId: z.string().trim(),
environment: z.string().trim(),
secretPath: z.string().trim().default("/"),
folderId: z.string().trim().optional(),
include_imports: z
.enum(["true", "false"])
.default("false")
.transform((value) => value === "true")
})
});
export const GetSecretByNameV3 = z.object({
query: z.object({
workspaceId: z.string().trim(),
environment: z.string().trim(),
secretPath: z.string().trim().default("/"),
type: z.enum([SECRET_SHARED, SECRET_PERSONAL]).optional()
}),
params: z.object({
secretName: z.string().trim()
})
});
export const CreateSecretV3 = z.object({
body: z.object({
workspaceId: z.string().trim(),
environment: z.string().trim(),
type: z.enum([SECRET_SHARED, SECRET_PERSONAL]),
secretPath: z.string().trim().default("/"),
secretName: z.string().trim(),
secretKeyCiphertext: z.string().trim(),
secretKeyIV: z.string().trim(),
secretKeyTag: z.string().trim(),
secretValueCiphertext: z.string().trim(),
secretValueIV: z.string().trim(),
secretValueTag: z.string().trim(),
secretCommentCiphertext: z.string().trim().optional(),
secretCommentIV: z.string().trim().optional(),
secretCommentTag: z.string().trim().optional(),
metadata: z.object({
source: z.string()
})
})
});
export const UpdateSecretByNameV3 = z.object({
body: z.object({
workspaceId: z.string().trim(),
environment: z.string().trim(),
type: z.enum([SECRET_SHARED, SECRET_PERSONAL]),
secretPath: z.string().trim().default("/"),
secretValueCiphertext: z.string().trim(),
secretValueIV: z.string().trim(),
secretValueTag: z.string().trim()
}),
params: z.object({
secretName: z.string()
})
});
export const DeleteSecretByNameV3 = z.object({
body: z.object({
workspaceId: z.string().trim(),
environment: z.string().trim(),
type: z.enum([SECRET_SHARED, SECRET_PERSONAL]),
secretPath: z.string().trim().default("/")
}),
params: z.object({
secretName: z.string()
})
});