Resolve merge conflict

This commit is contained in:
Tuan Dang
2023-11-23 12:02:41 +07:00
58 changed files with 1508 additions and 2602 deletions
@@ -1,4 +1,6 @@
import { Request, Response } from "express";
import { Types } from "mongoose";
import { Membership, User } from "../../../models";
import {
CreateRoleSchema,
DeleteRoleSchema,
@@ -11,7 +13,7 @@ import {
ProjectPermissionActions,
ProjectPermissionSub,
adminProjectPermissions,
getUserProjectPermissions,
getAuthDataProjectPermissions,
memberProjectPermissions,
viewerProjectPermission
} from "../../services/ProjectRoleService";
@@ -39,7 +41,10 @@ export const createRole = async (req: Request, res: Response) => {
throw BadRequestError({ message: "user doesn't have the permission." });
}
} else {
const { permission } = await getUserProjectPermissions(req.user.id, workspaceId);
const { permission } = await getAuthDataProjectPermissions({
authData: req.authData,
workspaceId: new Types.ObjectId(workspaceId)
});
if (permission.cannot(ProjectPermissionActions.Create, ProjectPermissionSub.Role)) {
throw BadRequestError({ message: "User doesn't have the permission." });
}
@@ -82,7 +87,11 @@ export const updateRole = async (req: Request, res: Response) => {
throw BadRequestError({ message: "User doesn't have the org permission." });
}
} else {
const { permission } = await getUserProjectPermissions(req.user.id, workspaceId);
const { permission } = await getAuthDataProjectPermissions({
authData: req.authData,
workspaceId: new Types.ObjectId(workspaceId)
});
if (permission.cannot(ProjectPermissionActions.Edit, ProjectPermissionSub.Role)) {
throw BadRequestError({ message: "User doesn't have the workspace permission." });
}
@@ -134,7 +143,11 @@ export const deleteRole = async (req: Request, res: Response) => {
throw BadRequestError({ message: "User doesn't have the org permission." });
}
} else {
const { permission } = await getUserProjectPermissions(req.user.id, role.workspace.toString());
const { permission } = await getAuthDataProjectPermissions({
authData: req.authData,
workspaceId: role.workspace
});
if (permission.cannot(ProjectPermissionActions.Delete, ProjectPermissionSub.Role)) {
throw BadRequestError({ message: "User doesn't have the workspace permission." });
}
@@ -162,7 +175,11 @@ export const getRoles = async (req: Request, res: Response) => {
throw BadRequestError({ message: "User doesn't have the org permission." });
}
} else {
const { permission } = await getUserProjectPermissions(req.user.id, workspaceId);
const { permission } = await getAuthDataProjectPermissions({
authData: req.authData,
workspaceId: new Types.ObjectId(workspaceId)
});
if (permission.cannot(ProjectPermissionActions.Read, ProjectPermissionSub.Role)) {
throw BadRequestError({ message: "User doesn't have the workspace permission." });
}
@@ -227,7 +244,19 @@ export const getUserWorkspacePermissions = async (req: Request, res: Response) =
const {
params: { workspaceId }
} = await validateRequest(GetUserProjectPermission, req);
const { permission, membership } = await getUserProjectPermissions(req.user._id, workspaceId);
const { permission } = await getAuthDataProjectPermissions({
authData: req.authData,
workspaceId: new Types.ObjectId(workspaceId)
});
let membership;
if (req.authData.authPayload instanceof User) {
membership = await Membership.findOne({
user: req.authData.authPayload._id,
workspace: new Types.ObjectId(workspaceId)
})
}
res.status(200).json({
data: {
@@ -1,10 +1,11 @@
import { Types } from "mongoose";
import { ForbiddenError, subject } from "@casl/ability";
import { Request, Response } from "express";
import { nanoid } from "nanoid";
import {
ProjectPermissionActions,
ProjectPermissionSub,
getUserProjectPermissions
getAuthDataProjectPermissions
} from "../../services/ProjectRoleService";
import { validateRequest } from "../../../helpers/validation";
import { SecretApprovalPolicy } from "../../models/secretApprovalPolicy";
@@ -19,7 +20,11 @@ export const createSecretApprovalPolicy = async (req: Request, res: Response) =>
body: { approvals, secretPath, approvers, environment, workspaceId, name }
} = await validateRequest(reqValidator.CreateSecretApprovalRule, req);
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
const { permission } = await getAuthDataProjectPermissions({
authData: req.authData,
workspaceId: new Types.ObjectId(workspaceId)
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Create,
ProjectPermissionSub.SecretApproval
@@ -49,10 +54,11 @@ export const updateSecretApprovalPolicy = async (req: Request, res: Response) =>
const secretApproval = await SecretApprovalPolicy.findById(id);
if (!secretApproval) throw ERR_SECRET_APPROVAL_NOT_FOUND;
const { permission } = await getUserProjectPermissions(
req.user._id,
secretApproval.workspace.toString()
);
const { permission } = await getAuthDataProjectPermissions({
authData: req.authData,
workspaceId: secretApproval.workspace
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Edit,
ProjectPermissionSub.SecretApproval
@@ -78,10 +84,11 @@ export const deleteSecretApprovalPolicy = async (req: Request, res: Response) =>
const secretApproval = await SecretApprovalPolicy.findById(id);
if (!secretApproval) throw ERR_SECRET_APPROVAL_NOT_FOUND;
const { permission } = await getUserProjectPermissions(
req.user._id,
secretApproval.workspace.toString()
);
const { permission } = await getAuthDataProjectPermissions({
authData: req.authData,
workspaceId: secretApproval.workspace
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Delete,
ProjectPermissionSub.SecretApproval
@@ -99,7 +106,11 @@ export const getSecretApprovalPolicy = async (req: Request, res: Response) => {
query: { workspaceId }
} = await validateRequest(reqValidator.GetSecretApprovalRuleList, req);
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
const { permission } = await getAuthDataProjectPermissions({
authData: req.authData,
workspaceId: new Types.ObjectId(workspaceId)
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Read,
ProjectPermissionSub.SecretApproval
@@ -117,7 +128,11 @@ export const getSecretApprovalPolicyOfBoard = async (req: Request, res: Response
query: { workspaceId, environment, secretPath }
} = await validateRequest(reqValidator.GetSecretApprovalPolicyOfABoard, req);
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
const { permission } = await getAuthDataProjectPermissions({
authData: req.authData,
workspaceId: new Types.ObjectId(workspaceId)
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Read,
subject(ProjectPermissionSub.Secrets, { secretPath, environment })
@@ -1,7 +1,6 @@
import { Request, Response } from "express";
import { getUserProjectPermissions } from "../../services/ProjectRoleService";
import { validateRequest } from "../../../helpers/validation";
import { Folder } from "../../../models";
import { Folder, Membership, User } from "../../../models";
import { ApprovalStatus, SecretApprovalRequest } from "../../models/secretApprovalRequest";
import * as reqValidator from "../../validation/secretApprovalRequest";
import { getFolderWithPathFromId } from "../../../services/FolderService";
@@ -17,7 +16,15 @@ export const getSecretApprovalRequestCount = async (req: Request, res: Response)
query: { workspaceId }
} = await validateRequest(reqValidator.getSecretApprovalRequestCount, req);
const { membership } = await getUserProjectPermissions(req.user._id, workspaceId);
if (!(req.authData.authPayload instanceof User)) return;
const membership = await Membership.findOne({
user: req.authData.authPayload._id,
workspace: new Types.ObjectId(workspaceId)
});
if (!membership) throw UnauthorizedRequestError();
const approvalRequestCount = await SecretApprovalRequest.aggregate([
{
$match: {
@@ -65,7 +72,14 @@ export const getSecretApprovalRequests = async (req: Request, res: Response) =>
query: { status, committer, workspaceId, environment, limit, offset }
} = await validateRequest(reqValidator.getSecretApprovalRequests, req);
const { membership } = await getUserProjectPermissions(req.user._id, workspaceId);
if (!(req.authData.authPayload instanceof User)) return;
const membership = await Membership.findOne({
user: req.authData.authPayload._id,
workspace: new Types.ObjectId(workspaceId)
});
if (!membership) throw UnauthorizedRequestError();
const query = {
workspace: new Types.ObjectId(workspaceId),
@@ -148,10 +162,15 @@ export const getSecretApprovalRequestDetails = async (req: Request, res: Respons
if (!secretApprovalRequest)
throw BadRequestError({ message: "Secret approval request not found" });
const { membership } = await getUserProjectPermissions(
req.user._id,
secretApprovalRequest.workspace.toString()
);
if (!(req.authData.authPayload instanceof User)) return;
const membership = await Membership.findOne({
user: req.authData.authPayload._id,
workspace: secretApprovalRequest.workspace
});
if (!membership) throw UnauthorizedRequestError();
// allow to fetch only if its admin or is the committer or approver
if (
membership.role !== "admin" &&
@@ -190,10 +209,15 @@ export const updateSecretApprovalReviewStatus = async (req: Request, res: Respon
if (!secretApprovalRequest)
throw BadRequestError({ message: "Secret approval request not found" });
const { membership } = await getUserProjectPermissions(
req.user._id,
secretApprovalRequest.workspace.toString()
);
if (!(req.authData.authPayload instanceof User)) return;
const membership = await Membership.findOne({
user: req.authData.authPayload._id,
workspace: secretApprovalRequest.workspace
});
if (!membership) throw UnauthorizedRequestError();
if (
membership.role !== "admin" &&
secretApprovalRequest.committer !== membership.id &&
@@ -227,10 +251,15 @@ export const mergeSecretApprovalRequest = async (req: Request, res: Response) =>
if (!secretApprovalRequest)
throw BadRequestError({ message: "Secret approval request not found" });
const { membership } = await getUserProjectPermissions(
req.user._id,
secretApprovalRequest.workspace.toString()
);
if (!(req.authData.authPayload instanceof User)) return;
const membership = await Membership.findOne({
user: req.authData.authPayload._id,
workspace: secretApprovalRequest.workspace
});
if (!membership) throw UnauthorizedRequestError();
if (
membership.role !== "admin" &&
secretApprovalRequest.committer !== membership.id &&
@@ -272,10 +301,14 @@ export const updateSecretApprovalRequestStatus = async (req: Request, res: Respo
if (!secretApprovalRequest)
throw BadRequestError({ message: "Secret approval request not found" });
const { membership } = await getUserProjectPermissions(
req.user._id,
secretApprovalRequest.workspace.toString()
);
if (!(req.authData.authPayload instanceof User)) return;
const membership = await Membership.findOne({
user: req.authData.authPayload._id,
workspace: secretApprovalRequest.workspace
});
if (!membership) throw UnauthorizedRequestError();
if (
membership.role !== "admin" &&
@@ -5,7 +5,7 @@ import { Folder, Secret } from "../../../models";
import {
ProjectPermissionActions,
ProjectPermissionSub,
getUserProjectPermissions
getAuthDataProjectPermissions
} from "../../services/ProjectRoleService";
import { BadRequestError } from "../../../utils/errors";
import * as reqValidator from "../../../validation";
@@ -74,7 +74,11 @@ export const getSecretVersions = async (req: Request, res: Response) => {
throw BadRequestError({ message: "Failed to find secret" });
}
const { permission } = await getUserProjectPermissions(req.user._id, secret.workspace.toString());
const { permission } = await getAuthDataProjectPermissions({
authData: req.authData,
workspaceId: secret.workspace
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Read,
ProjectPermissionSub.SecretRollback
@@ -157,10 +161,12 @@ export const rollbackSecretVersion = async (req: Request, res: Response) => {
if (!toBeUpdatedSec) {
throw BadRequestError({ message: "Failed to find secret" });
}
const { permission } = await getUserProjectPermissions(
req.user._id,
toBeUpdatedSec.workspace.toString()
);
const { permission } = await getAuthDataProjectPermissions({
authData: req.authData,
workspaceId: toBeUpdatedSec.workspace
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Create,
ProjectPermissionSub.SecretRollback
@@ -1,11 +1,12 @@
import { Request, Response } from "express";
import { Types } from "mongoose";
import { validateRequest } from "../../../helpers/validation";
import * as reqValidator from "../../validation/secretRotation";
import * as secretRotationService from "../../secretRotation/service";
import {
getUserProjectPermissions,
ProjectPermissionActions,
ProjectPermissionSub
ProjectPermissionSub,
getAuthDataProjectPermissions
} from "../../services/ProjectRoleService";
import { ForbiddenError } from "@casl/ability";
@@ -23,7 +24,11 @@ export const createSecretRotation = async (req: Request, res: Response) => {
}
} = await validateRequest(reqValidator.createSecretRotationV1, req);
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
const { permission } = await getAuthDataProjectPermissions({
authData: req.authData,
workspaceId: new Types.ObjectId(workspaceId)
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Create,
ProjectPermissionSub.SecretRotation
@@ -49,7 +54,12 @@ export const restartSecretRotations = async (req: Request, res: Response) => {
} = await validateRequest(reqValidator.restartSecretRotationV1, req);
const doc = await secretRotationService.getSecretRotationById({ id });
const { permission } = await getUserProjectPermissions(req.user._id, doc.workspace.toString());
const { permission } = await getAuthDataProjectPermissions({
authData: req.authData,
workspaceId: doc.workspace
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Edit,
ProjectPermissionSub.SecretRotation
@@ -65,7 +75,12 @@ export const deleteSecretRotations = async (req: Request, res: Response) => {
} = await validateRequest(reqValidator.removeSecretRotationV1, req);
const doc = await secretRotationService.getSecretRotationById({ id });
const { permission } = await getUserProjectPermissions(req.user._id, doc.workspace.toString());
const { permission } = await getAuthDataProjectPermissions({
authData: req.authData,
workspaceId: doc.workspace
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Delete,
ProjectPermissionSub.SecretRotation
@@ -80,7 +95,11 @@ export const getSecretRotations = async (req: Request, res: Response) => {
query: { workspaceId }
} = await validateRequest(reqValidator.getSecretRotationV1, req);
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
const { permission } = await getAuthDataProjectPermissions({
authData: req.authData,
workspaceId: new Types.ObjectId(workspaceId)
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Read,
ProjectPermissionSub.SecretRotation
@@ -1,11 +1,12 @@
import { Request, Response } from "express";
import { Types } from "mongoose";
import { validateRequest } from "../../../helpers/validation";
import * as reqValidator from "../../validation/secretRotationProvider";
import * as secretRotationProviderService from "../../secretRotation/service";
import {
getUserProjectPermissions,
ProjectPermissionActions,
ProjectPermissionSub
ProjectPermissionSub,
getAuthDataProjectPermissions
} from "../../services/ProjectRoleService";
import { ForbiddenError } from "@casl/ability";
@@ -14,7 +15,11 @@ export const getProviderTemplates = async (req: Request, res: Response) => {
params: { workspaceId }
} = await validateRequest(reqValidator.getSecretRotationProvidersV1, req);
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
const { permission } = await getAuthDataProjectPermissions({
authData: req.authData,
workspaceId: new Types.ObjectId(workspaceId)
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Read,
ProjectPermissionSub.SecretRotation
@@ -4,7 +4,7 @@ import { validateRequest } from "../../../helpers/validation";
import {
ProjectPermissionActions,
ProjectPermissionSub,
getUserProjectPermissions
getAuthDataProjectPermissions
} from "../../services/ProjectRoleService";
import * as reqValidator from "../../../validation/secretSnapshot";
import { ISecretVersion, SecretSnapshot, TFolderRootVersionSchema } from "../../models";
@@ -33,10 +33,11 @@ export const getSecretSnapshot = async (req: Request, res: Response) => {
if (!secretSnapshot) throw new Error("Failed to find secret snapshot");
const { permission } = await getUserProjectPermissions(
req.user._id,
secretSnapshot.workspace.toString()
);
const { permission } = await getAuthDataProjectPermissions({
authData: req.authData,
workspaceId: secretSnapshot.workspace
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Read,
ProjectPermissionSub.SecretRollback
@@ -27,7 +27,6 @@ import {
} from "../../models";
import { EESecretService } from "../../services";
import { getLatestSecretVersionIds } from "../../helpers/secretVersion";
// import Folder, { TFolderSchema } from "../../../models/folder";
import { getFolderByPath, searchByFolderId } from "../../../services/FolderService";
import { EEAuditLogService, EELicenseService } from "../../services";
import { extractIPDetails, isValidIpOrCidr } from "../../../utils/ip";
@@ -46,7 +45,7 @@ import {
import {
ProjectPermissionActions,
ProjectPermissionSub,
getUserProjectPermissions
getAuthDataProjectPermissions
} from "../../services/ProjectRoleService";
import { ForbiddenError } from "@casl/ability";
import { BadRequestError } from "../../../utils/errors";
@@ -107,7 +106,11 @@ export const getWorkspaceSecretSnapshots = async (req: Request, res: Response) =
query: { environment, directory, offset, limit }
} = await validateRequest(GetWorkspaceSecretSnapshotsV1, req);
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
const { permission } = await getAuthDataProjectPermissions({
authData: req.authData,
workspaceId: new Types.ObjectId(workspaceId)
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Read,
ProjectPermissionSub.SecretRollback
@@ -148,7 +151,11 @@ export const getWorkspaceSecretSnapshotsCount = async (req: Request, res: Respon
query: { environment, directory }
} = await validateRequest(GetWorkspaceSecretSnapshotsCountV1, req);
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
const { permission } = await getAuthDataProjectPermissions({
authData: req.authData,
workspaceId: new Types.ObjectId(workspaceId)
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Read,
ProjectPermissionSub.SecretRollback
@@ -238,7 +245,11 @@ export const rollbackWorkspaceSecretSnapshot = async (req: Request, res: Respons
body: { directory, environment, version }
} = await validateRequest(RollbackWorkspaceSecretSnapshotV1, req);
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
const { permission } = await getAuthDataProjectPermissions({
authData: req.authData,
workspaceId: new Types.ObjectId(workspaceId)
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Create,
ProjectPermissionSub.SecretRollback
@@ -649,7 +660,11 @@ export const getWorkspaceAuditLogs = async (req: Request, res: Response) => {
params: { workspaceId }
} = await validateRequest(GetWorkspaceAuditLogsV1, req);
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
const { permission } = await getAuthDataProjectPermissions({
authData: req.authData,
workspaceId: new Types.ObjectId(workspaceId)
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Read,
ProjectPermissionSub.AuditLogs
@@ -704,7 +719,11 @@ export const getWorkspaceAuditLogActorFilterOpts = async (req: Request, res: Res
params: { workspaceId }
} = await validateRequest(GetWorkspaceAuditLogActorFilterOptsV1, req);
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
const { permission } = await getAuthDataProjectPermissions({
authData: req.authData,
workspaceId: new Types.ObjectId(workspaceId)
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Read,
ProjectPermissionSub.AuditLogs
@@ -768,7 +787,11 @@ export const getWorkspaceTrustedIps = async (req: Request, res: Response) => {
params: { workspaceId }
} = await validateRequest(GetWorkspaceTrustedIpsV1, req);
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
const { permission } = await getAuthDataProjectPermissions({
authData: req.authData,
workspaceId: new Types.ObjectId(workspaceId)
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Read,
ProjectPermissionSub.IpAllowList
@@ -794,7 +817,11 @@ export const addWorkspaceTrustedIp = async (req: Request, res: Response) => {
body: { comment, isActive, ipAddress: ip }
} = await validateRequest(AddWorkspaceTrustedIpV1, req);
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
const { permission } = await getAuthDataProjectPermissions({
authData: req.authData,
workspaceId: new Types.ObjectId(workspaceId)
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Create,
ProjectPermissionSub.IpAllowList
@@ -860,7 +887,11 @@ export const updateWorkspaceTrustedIp = async (req: Request, res: Response) => {
body: { ipAddress: ip, comment }
} = await validateRequest(UpdateWorkspaceTrustedIpV1, req);
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
const { permission } = await getAuthDataProjectPermissions({
authData: req.authData,
workspaceId: new Types.ObjectId(workspaceId)
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Edit,
ProjectPermissionSub.IpAllowList
@@ -952,7 +983,11 @@ export const deleteWorkspaceTrustedIp = async (req: Request, res: Response) => {
params: { workspaceId, trustedIpId }
} = await validateRequest(DeleteWorkspaceTrustedIpV1, req);
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
const { permission } = await getAuthDataProjectPermissions({
authData: req.authData,
workspaceId: new Types.ObjectId(workspaceId)
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Delete,
ProjectPermissionSub.IpAllowList
@@ -8,13 +8,11 @@ import {
ServiceTokenDataV3Key,
Workspace
} from "../../../models";
import {
IServiceTokenV3Scope,
IServiceTokenV3TrustedIp
} from "../../../models/serviceTokenDataV3";
import { IServiceTokenV3TrustedIp } from "../../../models/serviceTokenDataV3";
import {
ActorType,
EventType
EventType,
Role
} from "../../models";
import { validateRequest } from "../../../helpers/validation";
import * as reqValidator from "../../../validation/serviceTokenDataV3";
@@ -22,14 +20,14 @@ import { createToken } from "../../../helpers/auth";
import {
ProjectPermissionActions,
ProjectPermissionSub,
getUserProjectPermissions
getAuthDataProjectPermissions
} from "../../services/ProjectRoleService";
import { ForbiddenError } from "@casl/ability";
import { BadRequestError, ResourceNotFoundError, UnauthorizedRequestError } from "../../../utils/errors";
import { extractIPDetails, isValidIpOrCidr } from "../../../utils/ip";
import { EEAuditLogService, EELicenseService } from "../../services";
import { getAuthSecret } from "../../../config";
import { AuthTokenType } from "../../../variables";
import { ADMIN, AuthTokenType, CUSTOM, MEMBER, VIEWER } from "../../../variables";
/**
* Return project key for service token V3
@@ -163,7 +161,7 @@ export const createServiceTokenData = async (req: Request, res: Response) => {
name,
workspaceId,
publicKey,
scopes,
role,
trustedIps,
expiresIn,
accessTokenTTL,
@@ -172,7 +170,11 @@ export const createServiceTokenData = async (req: Request, res: Response) => {
nonce, // for ServiceTokenDataV3Key
}
} = await validateRequest(reqValidator.CreateServiceTokenV3, req);
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
const { permission } = await getAuthDataProjectPermissions({
authData: req.authData,
workspaceId: new Types.ObjectId(workspaceId)
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Create,
ProjectPermissionSub.ServiceTokens
@@ -181,6 +183,19 @@ export const createServiceTokenData = async (req: Request, res: Response) => {
const workspace = await Workspace.findById(workspaceId);
if (!workspace) throw BadRequestError({ message: "Workspace not found" });
const isCustomRole = ![ADMIN, MEMBER, VIEWER].includes(role);
let customRole;
if (isCustomRole) {
customRole = await Role.findOne({
slug: role,
isOrgRole: false,
workspace: workspace._id
});
if (!customRole) throw BadRequestError({ message: "Role not found" });
}
const plan = await EELicenseService.getPlan(workspace.organization);
// validate trusted ips
@@ -219,7 +234,8 @@ export const createServiceTokenData = async (req: Request, res: Response) => {
accessTokenUsageCount: 0,
tokenVersion: 1,
trustedIps: reformattedTrustedIps,
scopes,
role: isCustomRole ? CUSTOM : role,
customRole,
isActive,
expiresAt,
accessTokenTTL,
@@ -250,7 +266,7 @@ export const createServiceTokenData = async (req: Request, res: Response) => {
metadata: {
name,
isActive,
scopes: scopes as Array<IServiceTokenV3Scope>,
role,
trustedIps: reformattedTrustedIps as Array<IServiceTokenV3TrustedIp>,
expiresAt
}
@@ -278,7 +294,7 @@ export const updateServiceTokenData = async (req: Request, res: Response) => {
body: {
name,
isActive,
scopes,
role,
trustedIps,
expiresIn,
accessTokenTTL,
@@ -291,10 +307,10 @@ export const updateServiceTokenData = async (req: Request, res: Response) => {
message: "Service token not found"
});
const { permission } = await getUserProjectPermissions(
req.user._id,
serviceTokenData.workspace.toString()
);
const { permission } = await getAuthDataProjectPermissions({
authData: req.authData,
workspaceId: serviceTokenData.workspace
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Edit,
@@ -304,6 +320,20 @@ export const updateServiceTokenData = async (req: Request, res: Response) => {
const workspace = await Workspace.findById(serviceTokenData.workspace);
if (!workspace) throw BadRequestError({ message: "Workspace not found" });
let customRole;
if (role) {
const isCustomRole = ![ADMIN, MEMBER, VIEWER].includes(role);
if (isCustomRole) {
customRole = await Role.findOne({
slug: role,
isOrgRole: false,
workspace: workspace._id
});
if (!customRole) throw BadRequestError({ message: "Role not found" });
}
}
const plan = await EELicenseService.getPlan(workspace.organization);
// validate trusted ips
@@ -335,7 +365,15 @@ export const updateServiceTokenData = async (req: Request, res: Response) => {
{
name,
isActive,
scopes,
role: customRole ? CUSTOM : role,
...(customRole ? {
customRole
} : {}),
...(role && !customRole ? { // non-custom role
$unset: {
customRole: 1
}
} : {}),
trustedIps: reformattedTrustedIps,
expiresAt,
accessTokenTTL,
@@ -357,7 +395,7 @@ export const updateServiceTokenData = async (req: Request, res: Response) => {
metadata: {
name: serviceTokenData.name,
isActive,
scopes: scopes as Array<IServiceTokenV3Scope>,
role,
trustedIps: reformattedTrustedIps as Array<IServiceTokenV3TrustedIp>,
expiresAt
}
@@ -388,10 +426,10 @@ export const deleteServiceTokenData = async (req: Request, res: Response) => {
message: "Service token not found"
});
const { permission } = await getUserProjectPermissions(
req.user._id,
serviceTokenData.workspace.toString()
);
const { permission } = await getAuthDataProjectPermissions({
authData: req.authData,
workspaceId: serviceTokenData.workspace
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Delete,
@@ -415,7 +453,7 @@ export const deleteServiceTokenData = async (req: Request, res: Response) => {
metadata: {
name: serviceTokenData.name,
isActive: serviceTokenData.isActive,
scopes: serviceTokenData.scopes as Array<IServiceTokenV3Scope>,
role: serviceTokenData.role,
trustedIps: serviceTokenData.trustedIps as Array<IServiceTokenV3TrustedIp>,
expiresAt: serviceTokenData.expiresAt
}
+1 -1
View File
@@ -2,7 +2,7 @@ export enum ActorType {
USER = "user",
SERVICE = "service",
SERVICE_V3 = "service-v3",
Machine = "machine"
// Machine = "machine"
}
export enum UserAgentType {
+8 -8
View File
@@ -1,5 +1,5 @@
import { ActorType, EventType } from "./enums";
import { IServiceTokenV3Scope, IServiceTokenV3TrustedIp } from "../../../models/serviceTokenDataV3";
import { IServiceTokenV3TrustedIp } from "../../../models/serviceTokenDataV3";
interface UserActorMetadata {
userId: string;
@@ -26,11 +26,11 @@ export interface ServiceActorV3 {
metadata: ServiceActorMetadata;
}
export interface MachineActor {
type: ActorType.Machine;
}
// export interface MachineActor {
// type: ActorType.Machine;
// }
export type Actor = UserActor | ServiceActor | ServiceActorV3 | MachineActor;
export type Actor = UserActor | ServiceActor | ServiceActorV3;
interface GetSecretsEvent {
type: EventType.GET_SECRETS;
@@ -225,7 +225,7 @@ interface CreateServiceTokenV3Event {
metadata: {
name: string;
isActive: boolean;
scopes: Array<IServiceTokenV3Scope>;
role: string;
trustedIps: Array<IServiceTokenV3TrustedIp>;
expiresAt?: Date;
};
@@ -236,7 +236,7 @@ interface UpdateServiceTokenV3Event {
metadata: {
name?: string;
isActive?: boolean;
scopes?: Array<IServiceTokenV3Scope>;
role?: string;
trustedIps?: Array<IServiceTokenV3TrustedIp>;
expiresAt?: Date;
};
@@ -247,7 +247,7 @@ interface DeleteServiceTokenV3Event {
metadata: {
name: string;
isActive: boolean;
scopes: Array<IServiceTokenV3Scope>;
role: string;
expiresAt?: Date;
trustedIps: Array<IServiceTokenV3TrustedIp>;
};
+89 -27
View File
@@ -1,3 +1,4 @@
import { Types } from "mongoose";
import {
AbilityBuilder,
ForcedSubject,
@@ -6,11 +7,14 @@ import {
buildMongoQueryMatcher,
createMongoAbility
} from "@casl/ability";
import { Membership } from "../../models";
import { IRole } from "../models/role";
import { BadRequestError, UnauthorizedRequestError } from "../../utils/errors";
import { UnauthorizedRequestError } from "../../utils/errors";
import { FieldCondition, FieldInstruction, JsInterpreter } from "@ucast/mongo2js";
import picomatch from "picomatch";
import { AuthData } from "../../interfaces/middleware";
import { ActorType, IRole } from "../models";
import { Membership, ServiceTokenData, ServiceTokenDataV3 } from "../../models";
import { ADMIN, CUSTOM, MEMBER, VIEWER } from "../../variables";
import { checkIPAgainstBlocklist } from "../../utils/ip";
const $glob: FieldInstruction<string> = {
type: "field",
@@ -239,31 +243,89 @@ const buildViewerPermission = () => {
export const viewerProjectPermission = buildViewerPermission();
export const getUserProjectPermissions = async (userId: string, workspaceId: string) => {
// TODO(akhilmhdh): speed this up by pulling from cache later
const membership = await Membership.findOne({
user: userId,
workspace: workspaceId
})
.populate<{
customRole: IRole & { permissions: RawRuleOf<MongoAbility<ProjectPermissionSet>>[] };
}>("customRole")
.exec();
/**
* Return permissions for user/service pertaining to workspace with id [workspaceId]
*
* Note: should not rely on this function for ST V2 authorization logic
* b/c ST V2 does not support role-based access control
*/
export const getAuthDataProjectPermissions = async ({
authData,
workspaceId
}: {
authData: AuthData;
workspaceId: Types.ObjectId;
}) => {
let role: "admin" | "member" | "viewer" | "custom";
let customRole;
switch (authData.actor.type) {
case ActorType.USER: {
const membership = await Membership.findOne({
user: authData.authPayload._id,
workspace: workspaceId
})
.populate<{
customRole: IRole & { permissions: RawRuleOf<MongoAbility<ProjectPermissionSet>>[] };
}>("customRole")
.exec();
if (!membership || (membership.role === "custom" && !membership.customRole)) {
throw UnauthorizedRequestError();
}
role = membership.role;
customRole = membership.customRole;
break;
}
case ActorType.SERVICE: {
const serviceTokenData = await ServiceTokenData.findById(authData.authPayload._id);
if (!serviceTokenData || !serviceTokenData.workspace.equals(workspaceId)) throw UnauthorizedRequestError();
role = "viewer";
break;
}
case ActorType.SERVICE_V3: {
const serviceTokenData = await ServiceTokenDataV3
.findById(authData.authPayload._id)
.populate<{
customRole: IRole & { permissions: RawRuleOf<MongoAbility<ProjectPermissionSet>>[] };
}>("customRole")
.exec();
if (!serviceTokenData || (serviceTokenData.role === "custom" && !serviceTokenData.customRole)) {
throw UnauthorizedRequestError();
}
if (!membership || (membership.role === "custom" && !membership.customRole)) {
throw UnauthorizedRequestError({ message: "User doesn't belong to organization" });
checkIPAgainstBlocklist({
ipAddress: authData.ipAddress,
trustedIps: serviceTokenData.trustedIps
});
role = serviceTokenData.role;
customRole = serviceTokenData.customRole;
break;
}
default:
throw UnauthorizedRequestError();
}
if (membership.role === "admin") return { permission: adminProjectPermissions, membership };
if (membership.role === "member") return { permission: memberProjectPermissions, membership };
if (membership.role === "viewer") return { permission: viewerProjectPermission, membership };
if (membership.role === "custom") {
const permission = createMongoAbility<ProjectPermissionSet>(membership.customRole.permissions, {
conditionsMatcher
});
return { permission, membership };
switch (role) {
case ADMIN:
return { permission: adminProjectPermissions };
case MEMBER:
return { permission: memberProjectPermissions };
case VIEWER:
return { permission: viewerProjectPermission };
case CUSTOM: {
if (!customRole) throw UnauthorizedRequestError();
return {
permission: createMongoAbility<ProjectPermissionSet>(
customRole.permissions,
{ conditionsMatcher }
)
};
}
default:
throw UnauthorizedRequestError();
}
throw BadRequestError({ message: "User role not found" });
};
}