mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-15 00:52:09 +00:00
227 lines
6.6 KiB
TypeScript
227 lines
6.6 KiB
TypeScript
import fs from "fs";
|
|
import path from "path";
|
|
import { Types } from "mongoose";
|
|
import {
|
|
IOrganization,
|
|
ISecret,
|
|
IServiceAccount,
|
|
IUser,
|
|
Membership,
|
|
} from "../models";
|
|
import { validateMembership } from "../helpers/membership";
|
|
import _ from "lodash";
|
|
import { BadRequestError, UnauthorizedRequestError, ValidationError } from "../utils/errors";
|
|
import {
|
|
validateMembershipOrg,
|
|
} from "../helpers/membershipOrg";
|
|
import {
|
|
PERMISSION_READ_SECRETS,
|
|
PERMISSION_WRITE_SECRETS,
|
|
} from "../variables";
|
|
|
|
/**
|
|
* Validate that email [email] is not disposable
|
|
* @param email - email to validate
|
|
*/
|
|
export const validateUserEmail = (email: string) => {
|
|
const emailDomain = email.split("@")[1];
|
|
const disposableEmails = fs.readFileSync(
|
|
path.resolve(__dirname, "../data/" + "disposable_emails.txt"),
|
|
"utf8"
|
|
).split("\n");
|
|
|
|
if (disposableEmails.includes(emailDomain)) throw ValidationError({
|
|
message: "Failed to validate email as non-disposable",
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Validate that user (client) can access workspace
|
|
* with id [workspaceId] and its environment [environment] with required permissions
|
|
* [requiredPermissions]
|
|
* @param {Object} obj
|
|
* @param {User} obj.user - user client
|
|
* @param {Types.ObjectId} obj.workspaceId - id of workspace to validate against
|
|
* @param {String} environment - (optional) environment in workspace to validate against
|
|
* @param {String[]} requiredPermissions - required permissions as part of the endpoint
|
|
*/
|
|
export const validateUserClientForWorkspace = async ({
|
|
user,
|
|
workspaceId,
|
|
environment,
|
|
acceptedRoles,
|
|
requiredPermissions,
|
|
}: {
|
|
user: IUser;
|
|
workspaceId: Types.ObjectId;
|
|
environment?: string;
|
|
acceptedRoles: Array<"admin" | "member">;
|
|
requiredPermissions?: string[];
|
|
}) => {
|
|
|
|
// validate user membership in workspace
|
|
const membership = await validateMembership({
|
|
userId: user._id,
|
|
workspaceId,
|
|
acceptedRoles,
|
|
});
|
|
|
|
let runningIsDisallowed = false;
|
|
requiredPermissions?.forEach((requiredPermission: string) => {
|
|
switch (requiredPermission) {
|
|
case PERMISSION_READ_SECRETS:
|
|
runningIsDisallowed = _.some(membership.deniedPermissions, { environmentSlug: environment, ability: PERMISSION_READ_SECRETS });
|
|
break;
|
|
case PERMISSION_WRITE_SECRETS:
|
|
runningIsDisallowed = _.some(membership.deniedPermissions, { environmentSlug: environment, ability: PERMISSION_WRITE_SECRETS });
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (runningIsDisallowed) {
|
|
throw UnauthorizedRequestError({
|
|
message: `Failed permissions authorization for workspace environment action : ${requiredPermission}`,
|
|
});
|
|
}
|
|
});
|
|
|
|
return membership;
|
|
}
|
|
|
|
/**
|
|
* Validate that user (client) can access secret [secret]
|
|
* with required permissions [requiredPermissions]
|
|
* @param {Object} obj
|
|
* @param {User} obj.user - user client
|
|
* @param {Secret[]} obj.secrets - secrets to validate against
|
|
* @param {String[]} requiredPermissions - required permissions as part of the endpoint
|
|
*/
|
|
export const validateUserClientForSecret = async ({
|
|
user,
|
|
secret,
|
|
acceptedRoles,
|
|
requiredPermissions,
|
|
}: {
|
|
user: IUser;
|
|
secret: ISecret;
|
|
acceptedRoles?: Array<"admin" | "member">;
|
|
requiredPermissions?: string[];
|
|
}) => {
|
|
const membership = await validateMembership({
|
|
userId: user._id,
|
|
workspaceId: secret.workspace,
|
|
acceptedRoles,
|
|
});
|
|
|
|
if (requiredPermissions?.includes(PERMISSION_WRITE_SECRETS)) {
|
|
const isDisallowed = _.some(membership.deniedPermissions, { environmentSlug: secret.environment, ability: PERMISSION_WRITE_SECRETS });
|
|
|
|
if (isDisallowed) {
|
|
throw UnauthorizedRequestError({
|
|
message: "You do not have the required permissions to perform this action",
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Validate that user (client) can access secrets [secrets]
|
|
* with required permissions [requiredPermissions]
|
|
* @param {Object} obj
|
|
* @param {User} obj.user - user client
|
|
* @param {Secret[]} obj.secrets - secrets to validate against
|
|
* @param {String[]} requiredPermissions - required permissions as part of the endpoint
|
|
*/
|
|
export const validateUserClientForSecrets = async ({
|
|
user,
|
|
secrets,
|
|
requiredPermissions,
|
|
}: {
|
|
user: IUser;
|
|
secrets: ISecret[];
|
|
requiredPermissions?: string[];
|
|
}) => {
|
|
|
|
// TODO: add acceptedRoles?
|
|
|
|
const userMemberships = await Membership.find({ user: user._id })
|
|
const userMembershipById = _.keyBy(userMemberships, "workspace");
|
|
const workspaceIdsSet = new Set(userMemberships.map((m) => m.workspace.toString()));
|
|
|
|
// for each secret check if the secret belongs to a workspace the user is a member of
|
|
secrets.forEach((secret: ISecret) => {
|
|
if (!workspaceIdsSet.has(secret.workspace.toString())) {
|
|
throw BadRequestError({
|
|
message: "Failed authorization for the secret",
|
|
});
|
|
}
|
|
|
|
if (requiredPermissions?.includes(PERMISSION_WRITE_SECRETS)) {
|
|
const deniedMembershipPermissions = userMembershipById[secret.workspace.toString()].deniedPermissions;
|
|
const isDisallowed = _.some(deniedMembershipPermissions, { environmentSlug: secret.environment, ability: PERMISSION_WRITE_SECRETS });
|
|
|
|
if (isDisallowed) {
|
|
throw UnauthorizedRequestError({
|
|
message: "You do not have the required permissions to perform this action",
|
|
});
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Validate that user (client) can access service account [serviceAccount]
|
|
* with required permissions [requiredPermissions]
|
|
* @param {Object} obj
|
|
* @param {User} obj.user - user client
|
|
* @param {ServiceAccount} obj.serviceAccount - service account to validate against
|
|
* @param {String[]} requiredPermissions - required permissions as part of the endpoint
|
|
*/
|
|
export const validateUserClientForServiceAccount = async ({
|
|
user,
|
|
serviceAccount,
|
|
requiredPermissions,
|
|
}: {
|
|
user: IUser;
|
|
serviceAccount: IServiceAccount;
|
|
requiredPermissions?: string[];
|
|
}) => {
|
|
if (!serviceAccount.user.equals(user._id)) {
|
|
// case: user who created service account is not the
|
|
// same user that is on the request
|
|
await validateMembershipOrg({
|
|
userId: user._id,
|
|
organizationId: serviceAccount.organization,
|
|
acceptedRoles: [],
|
|
acceptedStatuses: [],
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Validate that user (client) can access organization [organization]
|
|
* @param {Object} obj
|
|
* @param {User} obj.user - user client
|
|
* @param {Organization} obj.organization - organization to validate against
|
|
*/
|
|
export const validateUserClientForOrganization = async ({
|
|
user,
|
|
organization,
|
|
acceptedRoles,
|
|
acceptedStatuses,
|
|
}: {
|
|
user: IUser;
|
|
organization: IOrganization;
|
|
acceptedRoles: Array<"owner" | "admin" | "member">;
|
|
acceptedStatuses: Array<"invited" | "accepted">;
|
|
}) => {
|
|
const membershipOrg = await validateMembershipOrg({
|
|
userId: user._id,
|
|
organizationId: organization._id,
|
|
acceptedRoles,
|
|
acceptedStatuses,
|
|
});
|
|
|
|
return membershipOrg;
|
|
} |