Merge branch 'heads/main' into daniel/hsm-improvements-prep-work

This commit is contained in:
Daniel Hougaard
2025-10-22 15:11:39 +04:00
200 changed files with 5621 additions and 2275 deletions
+2
View File
@@ -48,12 +48,14 @@ import { registerSshCertRouter } from "./ssh-certificate-router";
import { registerSshCertificateTemplateRouter } from "./ssh-certificate-template-router";
import { registerSshHostGroupRouter } from "./ssh-host-group-router";
import { registerSshHostRouter } from "./ssh-host-router";
import { registerSubOrgRouter } from "./sub-org-router";
import { registerTrustedIpRouter } from "./trusted-ip-router";
import { registerUserAdditionalPrivilegeRouter } from "./user-additional-privilege-router";
export const registerV1EERoutes = async (server: FastifyZodProvider) => {
// org role starts with organization
await server.register(registerOrgRoleRouter, { prefix: "/organization" });
await server.register(registerSubOrgRouter, { prefix: "/sub-organizations" });
await server.register(registerLicenseRouter, { prefix: "/organizations" });
// depreciated in favour of infisical workspace
+1 -1
View File
@@ -58,7 +58,7 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
const plan = await server.services.license.getOrgPlan({
actorId: req.permission.id,
actor: req.permission.type,
actorOrgId: req.permission.orgId,
actorOrgId: req.permission.rootOrgId,
actorAuthMethod: req.permission.authMethod,
orgId: req.params.organizationId,
refreshCache: req.query.refreshCache
+34 -1
View File
@@ -3,12 +3,35 @@ import { z } from "zod";
import { AccessScope, OrgMembershipRole, OrgRolesSchema } from "@app/db/schemas";
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { OrgPermissionSchema } from "@app/ee/services/permission/org-permission";
import { OrgPermissionSchema, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
import { BadRequestError } from "@app/lib/errors";
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { slugSchema } from "@app/server/lib/schemas";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type";
const INVALID_SUBORG_PERMISSIONS = [
OrgPermissionSubjects.Sso,
OrgPermissionSubjects.Ldap,
OrgPermissionSubjects.Scim,
OrgPermissionSubjects.GithubOrgSync,
OrgPermissionSubjects.GithubOrgSyncManual,
OrgPermissionSubjects.Billing,
OrgPermissionSubjects.SubOrganization
];
const validateSubOrganizationSubjects = (permissions: unknown) => {
const invalidPermissionSubjects = (permissions as { subject: OrgPermissionSubjects }[])
.filter((el) => INVALID_SUBORG_PERMISSIONS.includes(el.subject))
.map((el) => el.subject);
if (invalidPermissionSubjects.length) {
const deduplication = Array.from(new Set(invalidPermissionSubjects));
throw new BadRequestError({
message: `Suborganization contains invalid permission subjects: ${deduplication.join(",")}`
});
}
};
export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
server.route({
method: "POST",
@@ -37,6 +60,11 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
},
onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => {
const isSubOrganization = req.permission.rootOrgId !== req.permission.orgId;
if (isSubOrganization) {
validateSubOrganizationSubjects(req.body.permissions);
}
const stringifiedPermissions = JSON.stringify(packRules(req.body.permissions));
const role = await server.services.role.createRole({
permission: req.permission,
@@ -133,6 +161,11 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
},
onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => {
const isSubOrganization = req.permission.rootOrgId !== req.permission.orgId;
if (isSubOrganization && req.body.permissions) {
validateSubOrganizationSubjects(req.body.permissions);
}
const stringifiedPermissions = req.body.permissions ? JSON.stringify(packRules(req.body.permissions)) : undefined;
const role = await server.services.role.updateRole({
permission: req.permission,
+163
View File
@@ -0,0 +1,163 @@
import { z } from "zod";
import { OrganizationsSchema } from "@app/db/schemas";
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { ApiDocsTags, SUB_ORGANIZATIONS } from "@app/lib/api-docs";
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { slugSchema } from "@app/server/lib/schemas";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type";
const sanitizedSubOrganizationSchema = OrganizationsSchema.pick({
id: true,
name: true,
slug: true,
createdAt: true,
updatedAt: true,
parentOrgId: true
});
export const registerSubOrgRouter = async (server: FastifyZodProvider) => {
server.route({
method: "POST",
url: "/",
config: {
rateLimit: writeLimit
},
schema: {
hide: false,
tags: [ApiDocsTags.SubOrganizations],
description: "Create a sub organization",
security: [
{
bearerAuth: []
}
],
body: z.object({
name: slugSchema().describe(SUB_ORGANIZATIONS.CREATE.name)
}),
response: {
200: z.object({
organization: sanitizedSubOrganizationSchema
})
}
},
onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => {
const { organization } = await server.services.subOrganization.createSubOrg({
name: req.body.name,
permissionActor: req.permission
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
orgId: req.permission.orgId,
event: {
type: EventType.CREATE_SUB_ORGANIZATION,
metadata: {
name: req.body.name,
organizationId: organization.id
}
}
});
return { organization };
}
});
server.route({
method: "GET",
url: "/",
config: {
rateLimit: readLimit
},
schema: {
hide: false,
tags: [ApiDocsTags.SubOrganizations],
description: "List of sub organizations",
security: [
{
bearerAuth: []
}
],
querystring: z.object({
limit: z.coerce.number().min(1).max(1000).default(25).describe(SUB_ORGANIZATIONS.LIST.limit),
offset: z.coerce.number().min(0).default(0).describe(SUB_ORGANIZATIONS.LIST.offset),
isAccessible: z
.enum(["true", "false"])
.optional()
.transform((value) => value === "true")
.describe(SUB_ORGANIZATIONS.LIST.isAccessible)
}),
response: {
200: z.object({
organizations: sanitizedSubOrganizationSchema.array()
})
}
},
onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => {
const { organizations } = await server.services.subOrganization.listSubOrgs({
permissionActor: req.permission,
data: {
limit: req.query.limit,
offset: req.query.offset,
isAccessible: req.query.isAccessible
}
});
return { organizations };
}
});
server.route({
method: "PATCH",
url: "/:subOrgId",
config: {
rateLimit: writeLimit
},
schema: {
hide: false,
tags: [ApiDocsTags.SubOrganizations],
description: "Update a sub organization",
security: [
{
bearerAuth: []
}
],
params: z.object({
subOrgId: z.string().trim().describe(SUB_ORGANIZATIONS.UPDATE.subOrgId)
}),
body: z.object({
name: slugSchema().describe(SUB_ORGANIZATIONS.UPDATE.name)
}),
response: {
200: z.object({
organization: sanitizedSubOrganizationSchema
})
}
},
onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => {
const { organization } = await server.services.subOrganization.updateSubOrg({
subOrgId: req.params.subOrgId,
name: req.body.name,
permissionActor: req.permission
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
orgId: req.permission.orgId,
event: {
type: EventType.UPDATE_SUB_ORGANIZATION,
metadata: {
name: req.body.name,
organizationId: organization.id
}
}
});
return { organization };
}
});
};
@@ -1,7 +1,7 @@
import { ForbiddenError } from "@casl/ability";
import { AxiosError } from "axios";
import { TAuditLogs } from "@app/db/schemas";
import { OrganizationActionScope, TAuditLogs } from "@app/db/schemas";
import {
decryptLogStream,
decryptLogStreamCredentials,
@@ -45,13 +45,14 @@ export const auditLogStreamServiceFactory = ({
});
}
const { permission } = await permissionService.getOrgPermission(
actor.type,
actor.id,
actor.orgId,
actor.authMethod,
actor.orgId
);
const { permission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.Any,
actor: actor.type,
actorId: actor.id,
orgId: actor.orgId,
actorAuthMethod: actor.authMethod,
actorOrgId: actor.orgId
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Settings);
@@ -94,13 +95,14 @@ export const auditLogStreamServiceFactory = ({
const logStream = await auditLogStreamDAL.findById(logStreamId);
if (!logStream) throw new NotFoundError({ message: `Audit Log Stream with ID '${logStreamId}' not found` });
const { permission } = await permissionService.getOrgPermission(
actor.type,
actor.id,
actor.orgId,
actor.authMethod,
logStream.orgId
);
const { permission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.Any,
actor: actor.type,
actorId: actor.id,
orgId: actor.orgId,
actorAuthMethod: actor.authMethod,
actorOrgId: actor.orgId
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Settings);
@@ -160,13 +162,14 @@ export const auditLogStreamServiceFactory = ({
const logStream = await auditLogStreamDAL.findById(logStreamId);
if (!logStream) throw new NotFoundError({ message: `Audit Log Stream with ID '${logStreamId}' not found` });
const { permission } = await permissionService.getOrgPermission(
actor.type,
actor.id,
actor.orgId,
actor.authMethod,
logStream.orgId
);
const { permission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.Any,
actor: actor.type,
actorId: actor.id,
orgId: actor.orgId,
actorAuthMethod: actor.authMethod,
actorOrgId: actor.orgId
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Delete, OrgPermissionSubjects.Settings);
@@ -185,14 +188,14 @@ export const auditLogStreamServiceFactory = ({
const logStream = await auditLogStreamDAL.findById(logStreamId);
if (!logStream) throw new NotFoundError({ message: `Audit log stream with ID '${logStreamId}' not found` });
const { permission } = await permissionService.getOrgPermission(
actor.type,
actor.id,
logStream.orgId,
actor.authMethod,
actor.orgId
);
const { permission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.Any,
actor: actor.type,
actorId: actor.id,
orgId: actor.orgId,
actorAuthMethod: actor.authMethod,
actorOrgId: actor.orgId
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Settings);
@@ -206,13 +209,14 @@ export const auditLogStreamServiceFactory = ({
};
const list = async (actor: OrgServiceActor) => {
const { permission } = await permissionService.getOrgPermission(
actor.type,
actor.id,
actor.orgId,
actor.authMethod,
actor.orgId
);
const { permission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.Any,
actor: actor.type,
actorId: actor.id,
orgId: actor.orgId,
actorAuthMethod: actor.authMethod,
actorOrgId: actor.orgId
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Settings);
@@ -1,7 +1,7 @@
import { ForbiddenError } from "@casl/ability";
import { requestContext } from "@fastify/request-context";
import { ActionProjectType } from "@app/db/schemas";
import { ActionProjectType, OrganizationActionScope } from "@app/db/schemas";
import { getConfig } from "@app/lib/config/env";
import { BadRequestError } from "@app/lib/errors";
import { ActorType } from "@app/services/auth/auth-type";
@@ -47,13 +47,14 @@ export const auditLogServiceFactory = ({
);
} else {
// Organization-wide logs
const { permission } = await permissionService.getOrgPermission(
const { permission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.Any,
actor,
actorId,
actorOrgId,
orgId: actorOrgId,
actorAuthMethod,
actorOrgId
);
});
ForbiddenError.from(permission).throwUnlessCan(
OrgPermissionAuditLogsActions.Read,
@@ -173,6 +173,9 @@ export enum EventType {
UPDATE_TOKEN_IDENTITY_TOKEN_AUTH = "update-token-identity-token-auth",
GET_TOKENS_IDENTITY_TOKEN_AUTH = "get-tokens-identity-token-auth",
CREATE_SUB_ORGANIZATION = "create-sub-organization",
UPDATE_SUB_ORGANIZATION = "update-sub-organization",
ADD_IDENTITY_TOKEN_AUTH = "add-identity-token-auth",
UPDATE_IDENTITY_TOKEN_AUTH = "update-identity-token-auth",
GET_IDENTITY_TOKEN_AUTH = "get-identity-token-auth",
@@ -616,6 +619,22 @@ interface GetSecretsEvent {
};
}
interface CreateSubOrganizationEvent {
type: EventType.CREATE_SUB_ORGANIZATION;
metadata: {
name: string;
organizationId: string;
};
}
interface UpdateSubOrganizationEvent {
type: EventType.UPDATE_SUB_ORGANIZATION;
metadata: {
name: string;
organizationId: string;
};
}
type TSecretMetadata = { key: string; value: string }[];
interface GetSecretEvent {
@@ -3964,6 +3983,8 @@ interface PamResourceDeleteEvent {
}
export type Event =
| CreateSubOrganizationEvent
| UpdateSubOrganizationEvent
| GetSecretsEvent
| GetSecretEvent
| CreateSecretEvent
@@ -1,6 +1,6 @@
import { ForbiddenError, subject } from "@casl/ability";
import { ActionProjectType } from "@app/db/schemas";
import { ActionProjectType, OrganizationActionScope } from "@app/db/schemas";
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
import {
@@ -134,13 +134,14 @@ export const dynamicSecretServiceFactory = ({
isGatewayV1 = false;
}
const { permission: orgPermission } = await permissionService.getOrgPermission(
const { permission: orgPermission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.Any,
actor,
actorId,
gateway?.orgId ?? gatewayv2?.orgId,
orgId: gateway?.orgId || gatewayv2?.orgId,
actorAuthMethod,
actorOrgId
);
});
ForbiddenError.from(orgPermission).throwUnlessCan(
OrgPermissionGatewayActions.AttachGateways,
@@ -297,13 +298,14 @@ export const dynamicSecretServiceFactory = ({
isGatewayV1 = false;
}
const { permission: orgPermission } = await permissionService.getOrgPermission(
const { permission: orgPermission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.Any,
actor,
actorId,
actorOrgId,
orgId: gateway?.orgId || gatewayv2?.orgId,
actorAuthMethod,
actorOrgId
);
});
ForbiddenError.from(orgPermission).throwUnlessCan(
OrgPermissionGatewayActions.AttachGateways,
@@ -3,6 +3,7 @@ import { STSServiceException } from "@aws-sdk/client-sts";
import { ForbiddenError } from "@casl/ability";
import slugify from "@sindresorhus/slugify";
import { OrganizationActionScope } from "@app/db/schemas";
import { BadRequestError, InternalServerError, NotFoundError } from "@app/lib/errors";
import { alphaNumericNanoId } from "@app/lib/nanoid";
import { TKmsKeyDALFactory } from "@app/services/kms/kms-key-dal";
@@ -51,13 +52,14 @@ export const externalKmsServiceFactory = ({
actorOrgId,
actorAuthMethod
}: TCreateExternalKmsDTO) => {
const { permission } = await permissionService.getOrgPermission(
const { permission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.Any,
actor,
actorId,
actorOrgId,
orgId: actorOrgId,
actorAuthMethod,
actorOrgId
);
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Kms);
const plan = await licenseService.getPlan(actorOrgId);
@@ -154,13 +156,14 @@ export const externalKmsServiceFactory = ({
actorAuthMethod
}: TUpdateExternalKmsDTO) => {
const kmsDoc = await kmsDAL.findById(kmsId);
const { permission } = await permissionService.getOrgPermission(
const { permission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.Any,
actor,
actorId,
kmsDoc.orgId,
orgId: kmsDoc.orgId,
actorAuthMethod,
actorOrgId
);
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Kms);
const plan = await licenseService.getPlan(kmsDoc.orgId);
@@ -257,13 +260,14 @@ export const externalKmsServiceFactory = ({
const deleteById = async ({ actor, id: kmsId, actorId, actorOrgId, actorAuthMethod }: TDeleteExternalKmsDTO) => {
const kmsDoc = await kmsDAL.findById(kmsId);
const { permission } = await permissionService.getOrgPermission(
const { permission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.Any,
actor,
actorId,
kmsDoc.orgId,
orgId: kmsDoc.orgId,
actorAuthMethod,
actorOrgId
);
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Delete, OrgPermissionSubjects.Kms);
const externalKmsDoc = await externalKmsDAL.findOne({ kmsKeyId: kmsDoc.id });
@@ -278,13 +282,14 @@ export const externalKmsServiceFactory = ({
};
const list = async ({ actor, actorId, actorOrgId, actorAuthMethod }: TListExternalKmsDTO) => {
const { permission } = await permissionService.getOrgPermission(
const { permission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.Any,
actor,
actorId,
actorOrgId,
orgId: actorOrgId,
actorAuthMethod,
actorOrgId
);
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Kms);
const externalKmsDocs = await externalKmsDAL.find({ orgId: actorOrgId });
@@ -294,13 +299,14 @@ export const externalKmsServiceFactory = ({
const findById = async ({ actor, actorId, actorOrgId, actorAuthMethod, id: kmsId }: TGetExternalKmsByIdDTO) => {
const kmsDoc = await kmsDAL.findById(kmsId);
const { permission } = await permissionService.getOrgPermission(
const { permission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.Any,
actor,
actorId,
kmsDoc.orgId,
orgId: kmsDoc.orgId,
actorAuthMethod,
actorOrgId
);
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Kms);
@@ -342,13 +348,14 @@ export const externalKmsServiceFactory = ({
name: kmsName
}: TGetExternalKmsBySlugDTO) => {
const kmsDoc = await kmsDAL.findOne({ name: kmsName, orgId: actorOrgId });
const { permission } = await permissionService.getOrgPermission(
const { permission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.Any,
actor,
actorId,
kmsDoc.orgId,
orgId: kmsDoc.orgId,
actorAuthMethod,
actorOrgId
);
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Kms);
const externalKmsDoc = await externalKmsDAL.findOne({ kmsKeyId: kmsDoc.id });
@@ -3,7 +3,7 @@ import net from "node:net";
import { ForbiddenError } from "@casl/ability";
import * as x509 from "@peculiar/x509";
import { OrgMembershipRole, TRelays } from "@app/db/schemas";
import { OrganizationActionScope, OrgMembershipRole, TRelays } from "@app/db/schemas";
import { PgSqlLock } from "@app/keystore/keystore";
import { crypto } from "@app/lib/crypto";
import { DatabaseErrorCode } from "@app/lib/error-codes";
@@ -73,13 +73,14 @@ export const gatewayV2ServiceFactory = ({
});
}
const { permission } = await permissionService.getOrgPermission(
ActorType.IDENTITY,
const { permission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.Any,
actor: ActorType.IDENTITY,
actorId,
orgId,
actorAuthMethod,
orgId
);
actorOrgId: orgId
});
ForbiddenError.from(permission).throwUnlessCan(
OrgPermissionGatewayActions.CreateGateways,
@@ -258,13 +259,14 @@ export const gatewayV2ServiceFactory = ({
};
const listGateways = async ({ orgPermission }: { orgPermission: OrgServiceActor }) => {
const { permission } = await permissionService.getOrgPermission(
orgPermission.type,
orgPermission.id,
orgPermission.orgId,
orgPermission.authMethod,
orgPermission.orgId
);
const { permission } = await permissionService.getOrgPermission({
actor: orgPermission.type,
actorId: orgPermission.id,
orgId: orgPermission.orgId,
actorAuthMethod: orgPermission.authMethod,
actorOrgId: orgPermission.orgId,
scope: OrganizationActionScope.Any
});
ForbiddenError.from(permission).throwUnlessCan(
OrgPermissionGatewayActions.ListGateways,
@@ -815,13 +817,14 @@ export const gatewayV2ServiceFactory = ({
throw new NotFoundError({ message: `Gateway ${id} not found` });
}
const { permission } = await permissionService.getOrgPermission(
orgPermission.type,
orgPermission.id,
gateway.orgId,
orgPermission.authMethod,
orgPermission.orgId
);
const { permission } = await permissionService.getOrgPermission({
actor: orgPermission.type,
actorId: orgPermission.id,
orgId: gateway.orgId,
actorAuthMethod: orgPermission.authMethod,
actorOrgId: orgPermission.orgId,
scope: OrganizationActionScope.Any
});
ForbiddenError.from(permission).throwUnlessCan(
OrgPermissionGatewayActions.DeleteGateways,
@@ -845,13 +848,14 @@ export const gatewayV2ServiceFactory = ({
};
const getPamSessionKey = async ({ orgPermission }: { orgPermission: OrgServiceActor }) => {
const { permission } = await permissionService.getOrgPermission(
orgPermission.type,
orgPermission.id,
orgPermission.orgId,
orgPermission.authMethod,
orgPermission.orgId
);
const { permission } = await permissionService.getOrgPermission({
actor: orgPermission.type,
actorId: orgPermission.id,
orgId: orgPermission.orgId,
actorAuthMethod: orgPermission.authMethod,
actorOrgId: orgPermission.orgId,
scope: OrganizationActionScope.Any
});
ForbiddenError.from(permission).throwUnlessCan(
OrgPermissionGatewayActions.CreateGateways,
@@ -2,6 +2,7 @@ import { ForbiddenError } from "@casl/ability";
import * as x509 from "@peculiar/x509";
import { z } from "zod";
import { OrganizationActionScope } from "@app/db/schemas";
import { KeyStorePrefixes, PgSqlLock, TKeyStoreFactory } from "@app/keystore/keystore";
import { getConfig } from "@app/lib/config/env";
import { crypto } from "@app/lib/crypto/cryptography";
@@ -68,13 +69,14 @@ export const gatewayServiceFactory = ({
"Gateway handshake failed due to organization plan restrictions. Please upgrade your instance to Infisical's Enterprise plan."
});
}
const { permission } = await permissionService.getOrgPermission(
ActorType.IDENTITY,
const { permission } = await permissionService.getOrgPermission({
actor: ActorType.IDENTITY,
actorId,
orgId,
actorAuthMethod,
orgId
);
actorOrgId: orgId,
scope: OrganizationActionScope.Any
});
ForbiddenError.from(permission).throwUnlessCan(
OrgPermissionGatewayActions.CreateGateways,
OrgPermissionSubjects.Gateway
@@ -480,13 +482,14 @@ export const gatewayServiceFactory = ({
};
const listGateways = async ({ orgPermission }: TListGatewaysDTO) => {
const { permission } = await permissionService.getOrgPermission(
orgPermission.type,
orgPermission.id,
orgPermission.orgId,
orgPermission.authMethod,
orgPermission.orgId
);
const { permission } = await permissionService.getOrgPermission({
actor: orgPermission.type,
actorId: orgPermission.id,
orgId: orgPermission.orgId,
actorAuthMethod: orgPermission.authMethod,
actorOrgId: orgPermission.orgId,
scope: OrganizationActionScope.Any
});
ForbiddenError.from(permission).throwUnlessCan(
OrgPermissionGatewayActions.ListGateways,
OrgPermissionSubjects.Gateway
@@ -501,13 +504,14 @@ export const gatewayServiceFactory = ({
};
const getGatewayById = async ({ orgPermission, id }: TGetGatewayByIdDTO) => {
const { permission } = await permissionService.getOrgPermission(
orgPermission.type,
orgPermission.id,
orgPermission.orgId,
orgPermission.authMethod,
orgPermission.orgId
);
const { permission } = await permissionService.getOrgPermission({
actor: orgPermission.type,
actorId: orgPermission.id,
orgId: orgPermission.orgId,
actorAuthMethod: orgPermission.authMethod,
actorOrgId: orgPermission.orgId,
scope: OrganizationActionScope.Any
});
ForbiddenError.from(permission).throwUnlessCan(
OrgPermissionGatewayActions.ListGateways,
OrgPermissionSubjects.Gateway
@@ -521,13 +525,14 @@ export const gatewayServiceFactory = ({
};
const updateGatewayById = async ({ orgPermission, id, name }: TUpdateGatewayByIdDTO) => {
const { permission } = await permissionService.getOrgPermission(
orgPermission.type,
orgPermission.id,
orgPermission.orgId,
orgPermission.authMethod,
orgPermission.orgId
);
const { permission } = await permissionService.getOrgPermission({
actor: orgPermission.type,
actorId: orgPermission.id,
orgId: orgPermission.orgId,
actorAuthMethod: orgPermission.authMethod,
actorOrgId: orgPermission.orgId,
scope: OrganizationActionScope.Any
});
ForbiddenError.from(permission).throwUnlessCan(
OrgPermissionGatewayActions.EditGateways,
OrgPermissionSubjects.Gateway
@@ -542,13 +547,14 @@ export const gatewayServiceFactory = ({
};
const deleteGatewayById = async ({ orgPermission, id }: TGetGatewayByIdDTO) => {
const { permission } = await permissionService.getOrgPermission(
orgPermission.type,
orgPermission.id,
orgPermission.orgId,
orgPermission.authMethod,
orgPermission.orgId
);
const { permission } = await permissionService.getOrgPermission({
actor: orgPermission.type,
actorId: orgPermission.id,
orgId: orgPermission.orgId,
actorAuthMethod: orgPermission.authMethod,
actorOrgId: orgPermission.orgId,
scope: OrganizationActionScope.Any
});
ForbiddenError.from(permission).throwUnlessCan(
OrgPermissionGatewayActions.DeleteGateways,
OrgPermissionSubjects.Gateway
@@ -6,7 +6,7 @@ import { paginateGraphql } from "@octokit/plugin-paginate-graphql";
import { Octokit as OctokitRest } from "@octokit/rest";
import RE2 from "re2";
import { AccessScope, OrgMembershipRole } from "@app/db/schemas";
import { AccessScope, OrganizationActionScope, OrgMembershipRole } from "@app/db/schemas";
import { BadRequestError, NotFoundError } from "@app/lib/errors";
import { groupBy } from "@app/lib/fn";
import { logger } from "@app/lib/logger";
@@ -104,13 +104,14 @@ export const githubOrgSyncServiceFactory = ({
githubOrgAccessToken,
isActive
}: TCreateGithubOrgSyncDTO) => {
const { permission } = await permissionService.getOrgPermission(
orgPermission.type,
orgPermission.id,
orgPermission.orgId,
orgPermission.authMethod,
orgPermission.orgId
);
const { permission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.ParentOrganization,
actor: orgPermission.type,
actorId: orgPermission.id,
orgId: orgPermission.orgId,
actorAuthMethod: orgPermission.authMethod,
actorOrgId: orgPermission.orgId
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.GithubOrgSync);
const plan = await licenseService.getPlan(orgPermission.orgId);
@@ -162,13 +163,14 @@ export const githubOrgSyncServiceFactory = ({
githubOrgAccessToken,
isActive
}: TUpdateGithubOrgSyncDTO) => {
const { permission } = await permissionService.getOrgPermission(
orgPermission.type,
orgPermission.id,
orgPermission.orgId,
orgPermission.authMethod,
orgPermission.orgId
);
const { permission } = await permissionService.getOrgPermission({
actor: orgPermission.type,
scope: OrganizationActionScope.ParentOrganization,
actorId: orgPermission.id,
orgId: orgPermission.orgId,
actorAuthMethod: orgPermission.authMethod,
actorOrgId: orgPermission.orgId
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.GithubOrgSync);
const plan = await licenseService.getPlan(orgPermission.orgId);
@@ -226,13 +228,14 @@ export const githubOrgSyncServiceFactory = ({
};
const deleteGithubOrgSync = async ({ orgPermission }: TDeleteGithubOrgSyncDTO) => {
const { permission } = await permissionService.getOrgPermission(
orgPermission.type,
orgPermission.id,
orgPermission.orgId,
orgPermission.authMethod,
orgPermission.orgId
);
const { permission } = await permissionService.getOrgPermission({
actor: orgPermission.type,
actorId: orgPermission.id,
orgId: orgPermission.orgId,
actorAuthMethod: orgPermission.authMethod,
actorOrgId: orgPermission.orgId,
scope: OrganizationActionScope.ParentOrganization
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Delete, OrgPermissionSubjects.GithubOrgSync);
@@ -256,13 +259,14 @@ export const githubOrgSyncServiceFactory = ({
};
const getGithubOrgSync = async ({ orgPermission }: TDeleteGithubOrgSyncDTO) => {
const { permission } = await permissionService.getOrgPermission(
orgPermission.type,
orgPermission.id,
orgPermission.orgId,
orgPermission.authMethod,
orgPermission.orgId
);
const { permission } = await permissionService.getOrgPermission({
actorId: orgPermission.id,
actor: orgPermission.type,
orgId: orgPermission.orgId,
actorAuthMethod: orgPermission.authMethod,
actorOrgId: orgPermission.orgId,
scope: OrganizationActionScope.ParentOrganization
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.GithubOrgSync);
@@ -422,13 +426,14 @@ export const githubOrgSyncServiceFactory = ({
};
const validateGithubToken = async ({ orgPermission, githubOrgAccessToken }: TValidateGithubTokenDTO) => {
const { permission } = await permissionService.getOrgPermission(
orgPermission.type,
orgPermission.id,
orgPermission.orgId,
orgPermission.authMethod,
orgPermission.orgId
);
const { permission } = await permissionService.getOrgPermission({
actorId: orgPermission.id,
actor: orgPermission.type,
orgId: orgPermission.orgId,
actorAuthMethod: orgPermission.authMethod,
actorOrgId: orgPermission.orgId,
scope: OrganizationActionScope.ParentOrganization
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.GithubOrgSync);
@@ -509,13 +514,14 @@ export const githubOrgSyncServiceFactory = ({
};
const syncAllTeams = async ({ orgPermission }: TSyncAllTeamsDTO): Promise<TSyncResult> => {
const { permission } = await permissionService.getOrgPermission(
orgPermission.type,
orgPermission.id,
orgPermission.orgId,
orgPermission.authMethod,
orgPermission.orgId
);
const { permission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.ParentOrganization,
actor: orgPermission.type,
orgId: orgPermission.orgId,
actorId: orgPermission.id,
actorAuthMethod: orgPermission.authMethod,
actorOrgId: orgPermission.orgId
});
ForbiddenError.from(permission).throwUnlessCan(
OrgPermissionActions.Edit,
+31 -24
View File
@@ -1,7 +1,7 @@
import { ForbiddenError } from "@casl/ability";
import slugify from "@sindresorhus/slugify";
import { AccessScope, OrgMembershipRole, TRoles } from "@app/db/schemas";
import { AccessScope, OrganizationActionScope, OrgMembershipRole, TRoles } from "@app/db/schemas";
import { TOidcConfigDALFactory } from "@app/ee/services/oidc/oidc-config-dal";
import { BadRequestError, NotFoundError, PermissionBoundaryError, UnauthorizedError } from "@app/lib/errors";
import { alphaNumericNanoId } from "@app/lib/nanoid";
@@ -73,13 +73,14 @@ export const groupServiceFactory = ({
const createGroup = async ({ name, slug, role, actor, actorId, actorAuthMethod, actorOrgId }: TCreateGroupDTO) => {
if (!actorOrgId) throw new UnauthorizedError({ message: "No organization ID provided in request" });
const { permission } = await permissionService.getOrgPermission(
const { permission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.Any,
actor,
actorId,
actorOrgId,
orgId: actorOrgId,
actorAuthMethod,
actorOrgId
);
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionGroupActions.Create, OrgPermissionSubjects.Groups);
const plan = await licenseService.getPlan(actorOrgId);
@@ -167,13 +168,14 @@ export const groupServiceFactory = ({
}: TUpdateGroupDTO) => {
if (!actorOrgId) throw new UnauthorizedError({ message: "No organization ID provided in request" });
const { permission } = await permissionService.getOrgPermission(
const { permission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.Any,
actor,
actorId,
actorOrgId,
orgId: actorOrgId,
actorAuthMethod,
actorOrgId
);
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionGroupActions.Edit, OrgPermissionSubjects.Groups);
@@ -270,13 +272,14 @@ export const groupServiceFactory = ({
const deleteGroup = async ({ id, actor, actorId, actorAuthMethod, actorOrgId }: TDeleteGroupDTO) => {
if (!actorOrgId) throw new UnauthorizedError({ message: "No organization ID provided in request" });
const { permission } = await permissionService.getOrgPermission(
const { permission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.Any,
actor,
actorId,
actorOrgId,
orgId: actorOrgId,
actorAuthMethod,
actorOrgId
);
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionGroupActions.Delete, OrgPermissionSubjects.Groups);
const plan = await licenseService.getPlan(actorOrgId);
@@ -297,17 +300,18 @@ export const groupServiceFactory = ({
const getGroupById = async ({ id, actor, actorId, actorAuthMethod, actorOrgId }: TGetGroupByIdDTO) => {
if (!actorOrgId) throw new UnauthorizedError({ message: "No organization ID provided in request" });
const { permission } = await permissionService.getOrgPermission(
const { permission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.Any,
actor,
actorId,
actorOrgId,
orgId: actorOrgId,
actorAuthMethod,
actorOrgId
);
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionGroupActions.Read, OrgPermissionSubjects.Groups);
const group = await groupDAL.findById(id);
if (!group) {
if (!group || group.orgId !== actorOrgId) {
throw new NotFoundError({
message: `Cannot find group with ID ${id}`
});
@@ -330,13 +334,14 @@ export const groupServiceFactory = ({
}: TListGroupUsersDTO) => {
if (!actorOrgId) throw new UnauthorizedError({ message: "No organization ID provided in request" });
const { permission } = await permissionService.getOrgPermission(
const { permission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.Any,
actor,
actorId,
actorOrgId,
orgId: actorOrgId,
actorAuthMethod,
actorOrgId
);
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionGroupActions.Read, OrgPermissionSubjects.Groups);
const group = await groupDAL.findOne({
@@ -365,13 +370,14 @@ export const groupServiceFactory = ({
const addUserToGroup = async ({ id, username, actor, actorId, actorAuthMethod, actorOrgId }: TAddUserToGroupDTO) => {
if (!actorOrgId) throw new UnauthorizedError({ message: "No organization ID provided in request" });
const { permission } = await permissionService.getOrgPermission(
const { permission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.Any,
actor,
actorId,
actorOrgId,
orgId: actorOrgId,
actorAuthMethod,
actorOrgId
);
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionGroupActions.Edit, OrgPermissionSubjects.Groups);
// check if group with slug exists
@@ -451,13 +457,14 @@ export const groupServiceFactory = ({
}: TRemoveUserFromGroupDTO) => {
if (!actorOrgId) throw new UnauthorizedError({ message: "No organization ID provided in request" });
const { permission } = await permissionService.getOrgPermission(
const { permission } = await permissionService.getOrgPermission({
actor,
actorId,
actorOrgId,
orgId: actorOrgId,
actorAuthMethod,
actorOrgId
);
actorOrgId,
scope: OrganizationActionScope.Any
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionGroupActions.Edit, OrgPermissionSubjects.Groups);
// check if group with slug exists
@@ -1,5 +1,6 @@
import { ForbiddenError } from "@casl/ability";
import { OrganizationActionScope } from "@app/db/schemas";
import { EventType, TAuditLogServiceFactory } from "@app/ee/services/audit-log/audit-log-types";
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
import {
@@ -68,13 +69,14 @@ export const identityAuthTemplateServiceFactory = ({
templateFields: Record<string, unknown>;
} & Omit<TOrgPermission, "orgId">) => {
await $checkPlan(actorOrgId);
const { permission } = await permissionService.getOrgPermission(
const { permission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.Any,
actor,
actorId,
actorOrgId,
orgId: actorOrgId,
actorAuthMethod,
actorOrgId
);
});
ForbiddenError.from(permission).throwUnlessCan(
OrgPermissionMachineIdentityAuthTemplateActions.CreateTemplates,
OrgPermissionSubjects.MachineIdentityAuthTemplate
@@ -113,13 +115,14 @@ export const identityAuthTemplateServiceFactory = ({
throw new NotFoundError({ message: "Template not found" });
}
const { permission } = await permissionService.getOrgPermission(
const { permission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.Any,
actor,
actorId,
template.orgId,
orgId: template.orgId,
actorAuthMethod,
actorOrgId
);
});
ForbiddenError.from(permission).throwUnlessCan(
OrgPermissionMachineIdentityAuthTemplateActions.EditTemplates,
OrgPermissionSubjects.MachineIdentityAuthTemplate
@@ -227,13 +230,14 @@ export const identityAuthTemplateServiceFactory = ({
throw new NotFoundError({ message: "Template not found" });
}
const { permission } = await permissionService.getOrgPermission(
const { permission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.Any,
actor,
actorId,
template.orgId,
orgId: template.orgId,
actorAuthMethod,
actorOrgId
);
});
ForbiddenError.from(permission).throwUnlessCan(
OrgPermissionMachineIdentityAuthTemplateActions.DeleteTemplates,
OrgPermissionSubjects.MachineIdentityAuthTemplate
@@ -282,13 +286,14 @@ export const identityAuthTemplateServiceFactory = ({
throw new NotFoundError({ message: "Template not found" });
}
const { permission } = await permissionService.getOrgPermission(
const { permission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.Any,
actor,
actorId,
template.orgId,
orgId: template.orgId,
actorAuthMethod,
actorOrgId
);
});
ForbiddenError.from(permission).throwUnlessCan(
OrgPermissionMachineIdentityAuthTemplateActions.ListTemplates,
OrgPermissionSubjects.MachineIdentityAuthTemplate
@@ -316,13 +321,14 @@ export const identityAuthTemplateServiceFactory = ({
actorOrgId
}: TListIdentityAuthTemplatesDTO) => {
await $checkPlan(actorOrgId);
const { permission } = await permissionService.getOrgPermission(
const { permission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.Any,
actor,
actorId,
actorOrgId,
orgId: actorOrgId,
actorAuthMethod,
actorOrgId
);
});
ForbiddenError.from(permission).throwUnlessCan(
OrgPermissionMachineIdentityAuthTemplateActions.ListTemplates,
OrgPermissionSubjects.MachineIdentityAuthTemplate
@@ -352,13 +358,14 @@ export const identityAuthTemplateServiceFactory = ({
actorOrgId
}: TGetTemplatesByAuthMethodDTO) => {
await $checkPlan(actorOrgId);
const { permission } = await permissionService.getOrgPermission(
const { permission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.Any,
actor,
actorId,
actorOrgId,
orgId: actorOrgId,
actorAuthMethod,
actorOrgId
);
});
ForbiddenError.from(permission).throwUnlessCan(
OrgPermissionMachineIdentityAuthTemplateActions.AttachTemplates,
OrgPermissionSubjects.MachineIdentityAuthTemplate
@@ -385,13 +392,14 @@ export const identityAuthTemplateServiceFactory = ({
actorOrgId
}: TFindTemplateUsagesDTO) => {
await $checkPlan(actorOrgId);
const { permission } = await permissionService.getOrgPermission(
const { permission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.Any,
actor,
actorId,
actorOrgId,
orgId: actorOrgId,
actorAuthMethod,
actorOrgId
);
});
ForbiddenError.from(permission).throwUnlessCan(
OrgPermissionMachineIdentityAuthTemplateActions.ListTemplates,
OrgPermissionSubjects.MachineIdentityAuthTemplate
@@ -415,13 +423,14 @@ export const identityAuthTemplateServiceFactory = ({
actorOrgId
}: TUnlinkTemplateUsageDTO) => {
await $checkPlan(actorOrgId);
const { permission } = await permissionService.getOrgPermission(
const { permission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.Any,
actor,
actorId,
actorOrgId,
orgId: actorOrgId,
actorAuthMethod,
actorOrgId
);
});
ForbiddenError.from(permission).throwUnlessCan(
OrgPermissionMachineIdentityAuthTemplateActions.UnlinkTemplates,
OrgPermissionSubjects.MachineIdentityAuthTemplate
@@ -1,5 +1,6 @@
import { ForbiddenError } from "@casl/ability";
import { OrganizationActionScope } from "@app/db/schemas";
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
import { TKmsKeyDALFactory } from "@app/services/kms/kms-key-dal";
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
@@ -46,13 +47,14 @@ export const kmipOperationServiceFactory = ({
actorAuthMethod,
actorOrgId
}: TKmipCreateDTO) => {
const { permission } = await permissionService.getOrgPermission(
const { permission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.Any,
actor,
actorId,
actorOrgId,
orgId: actorOrgId,
actorAuthMethod,
actorOrgId
);
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionKmipActions.Proxy, OrgPermissionSubjects.Kmip);
@@ -78,13 +80,14 @@ export const kmipOperationServiceFactory = ({
};
const destroy = async ({ projectId, id, clientId, actor, actorId, actorOrgId, actorAuthMethod }: TKmipDestroyDTO) => {
const { permission } = await permissionService.getOrgPermission(
const { permission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.Any,
actor,
actorId,
actorOrgId,
orgId: actorOrgId,
actorAuthMethod,
actorOrgId
);
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionKmipActions.Proxy, OrgPermissionSubjects.Kmip);
@@ -131,13 +134,14 @@ export const kmipOperationServiceFactory = ({
};
const get = async ({ projectId, id, clientId, actor, actorId, actorAuthMethod, actorOrgId }: TKmipGetDTO) => {
const { permission } = await permissionService.getOrgPermission(
const { permission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.Any,
actor,
actorId,
actorOrgId,
orgId: actorOrgId,
actorAuthMethod,
actorOrgId
);
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionKmipActions.Proxy, OrgPermissionSubjects.Kmip);
@@ -189,13 +193,14 @@ export const kmipOperationServiceFactory = ({
};
const activate = async ({ projectId, id, clientId, actor, actorId, actorAuthMethod, actorOrgId }: TKmipGetDTO) => {
const { permission } = await permissionService.getOrgPermission(
const { permission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.Any,
actor,
actorId,
actorOrgId,
orgId: actorOrgId,
actorAuthMethod,
actorOrgId
);
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionKmipActions.Proxy, OrgPermissionSubjects.Kmip);
@@ -226,13 +231,14 @@ export const kmipOperationServiceFactory = ({
};
const revoke = async ({ projectId, id, clientId, actor, actorId, actorAuthMethod, actorOrgId }: TKmipRevokeDTO) => {
const { permission } = await permissionService.getOrgPermission(
const { permission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.Any,
actor,
actorId,
actorOrgId,
orgId: actorOrgId,
actorAuthMethod,
actorOrgId
);
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionKmipActions.Proxy, OrgPermissionSubjects.Kmip);
@@ -287,13 +293,14 @@ export const kmipOperationServiceFactory = ({
actorAuthMethod,
actorOrgId
}: TKmipGetAttributesDTO) => {
const { permission } = await permissionService.getOrgPermission(
const { permission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.Any,
actor,
actorId,
actorOrgId,
orgId: actorOrgId,
actorAuthMethod,
actorOrgId
);
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionKmipActions.Proxy, OrgPermissionSubjects.Kmip);
@@ -339,13 +346,14 @@ export const kmipOperationServiceFactory = ({
};
const locate = async ({ projectId, clientId, actor, actorId, actorAuthMethod, actorOrgId }: TKmipLocateDTO) => {
const { permission } = await permissionService.getOrgPermission(
const { permission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.Any,
actor,
actorId,
actorOrgId,
orgId: actorOrgId,
actorAuthMethod,
actorOrgId
);
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionKmipActions.Proxy, OrgPermissionSubjects.Kmip);
@@ -377,13 +385,14 @@ export const kmipOperationServiceFactory = ({
actorOrgId,
kmipMetadata
}: TKmipRegisterDTO) => {
const { permission } = await permissionService.getOrgPermission(
const { permission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.Any,
actor,
actorId,
actorOrgId,
orgId: actorOrgId,
actorAuthMethod,
actorOrgId
);
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionKmipActions.Proxy, OrgPermissionSubjects.Kmip);
+17 -8
View File
@@ -1,7 +1,7 @@
import { ForbiddenError } from "@casl/ability";
import * as x509 from "@peculiar/x509";
import { ActionProjectType } from "@app/db/schemas";
import { ActionProjectType, OrganizationActionScope } from "@app/db/schemas";
import { crypto } from "@app/lib/crypto/cryptography";
import { BadRequestError, InternalServerError, NotFoundError } from "@app/lib/errors";
import { isValidIp } from "@app/lib/ip";
@@ -401,13 +401,14 @@ export const kmipServiceFactory = ({
};
const setupOrgKmip = async ({ caKeyAlgorithm, actorOrgId, actor, actorId, actorAuthMethod }: TSetupOrgKmipDTO) => {
const { permission } = await permissionService.getOrgPermission(
const { permission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.Any,
actor,
actorId,
actorOrgId,
orgId: actorOrgId,
actorAuthMethod,
actorOrgId
);
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionKmipActions.Setup, OrgPermissionSubjects.Kmip);
const kmipConfig = await kmipOrgConfigDAL.findOne({
@@ -566,7 +567,14 @@ export const kmipServiceFactory = ({
};
const getOrgKmip = async ({ actorOrgId, actor, actorId, actorAuthMethod }: TGetOrgKmipDTO) => {
await permissionService.getOrgPermission(actor, actorId, actorOrgId, actorAuthMethod, actorOrgId);
await permissionService.getOrgPermission({
scope: OrganizationActionScope.Any,
actor,
actorId,
orgId: actorOrgId,
actorAuthMethod,
actorOrgId
});
const kmipConfig = await kmipOrgConfigDAL.findOne({
orgId: actorOrgId
@@ -759,13 +767,14 @@ export const kmipServiceFactory = ({
keyAlgorithm,
hostnamesOrIps
}: TRegisterServerDTO) => {
const { permission } = await permissionService.getOrgPermission(
const { permission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.Any,
actor,
actorId,
actorOrgId,
orgId: actorOrgId,
actorAuthMethod,
actorOrgId
);
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionKmipActions.Proxy, OrgPermissionSubjects.Kmip);
@@ -1,7 +1,14 @@
import { ForbiddenError } from "@casl/ability";
import { Knex } from "knex";
import { AccessScope, OrgMembershipStatus, TableName, TLdapConfigsUpdate, TUsers } from "@app/db/schemas";
import {
AccessScope,
OrganizationActionScope,
OrgMembershipStatus,
TableName,
TLdapConfigsUpdate,
TUsers
} from "@app/db/schemas";
import { TGroupDALFactory } from "@app/ee/services/group/group-dal";
import { addUsersToGroupByUserIds, removeUsersFromGroupByUserIds } from "@app/ee/services/group/group-fns";
import { TUserGroupMembershipDALFactory } from "@app/ee/services/group/user-group-membership-dal";
@@ -119,7 +126,14 @@ export const ldapConfigServiceFactory = ({
groupSearchFilter,
caCert
}: TCreateLdapCfgDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
const { permission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.ParentOrganization,
actor,
actorId,
orgId: actorOrgId,
actorAuthMethod,
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Ldap);
const plan = await licenseService.getPlan(orgId);
@@ -238,7 +252,14 @@ export const ldapConfigServiceFactory = ({
groupSearchFilter,
caCert
}: TUpdateLdapCfgDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
const { permission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.ParentOrganization,
actor,
actorId,
orgId: actorOrgId,
actorAuthMethod,
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Ldap);
const plan = await licenseService.getPlan(orgId);
@@ -316,7 +337,14 @@ export const ldapConfigServiceFactory = ({
actorAuthMethod,
actorOrgId
}: TGetLdapCfgDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
const { permission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.ParentOrganization,
actor,
actorId,
orgId: actorOrgId,
actorAuthMethod,
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Ldap);
return getLdapCfg({
orgId
@@ -649,7 +677,14 @@ export const ldapConfigServiceFactory = ({
actorAuthMethod,
actorOrgId
}: TGetLdapGroupMapsDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
const { permission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.ParentOrganization,
actor,
actorId,
orgId: actorOrgId,
actorAuthMethod,
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Ldap);
const ldapConfig = await ldapConfigDAL.findOne({
@@ -678,7 +713,14 @@ export const ldapConfigServiceFactory = ({
actorAuthMethod,
actorOrgId
}: TCreateLdapGroupMapDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
const { permission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.ParentOrganization,
actor,
actorId,
orgId: actorOrgId,
actorAuthMethod,
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Ldap);
const plan = await licenseService.getPlan(orgId);
@@ -732,7 +774,14 @@ export const ldapConfigServiceFactory = ({
actorAuthMethod,
actorOrgId
}: TDeleteLdapGroupMapDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
const { permission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.ParentOrganization,
actor,
actorId,
orgId: actorOrgId,
actorAuthMethod,
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Delete, OrgPermissionSubjects.Ldap);
const plan = await licenseService.getPlan(orgId);
@@ -771,7 +820,14 @@ export const ldapConfigServiceFactory = ({
caCert,
url
}: TTestLdapConnectionDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
const { permission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.ParentOrganization,
actor,
actorId,
orgId: actorOrgId,
actorAuthMethod,
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Ldap);
const plan = await licenseService.getPlan(orgId);
+28 -5
View File
@@ -10,6 +10,7 @@ export const licenseDALFactory = (db: TDbClient) => {
const countOfOrgMembers = async (orgId: string | null, tx?: Knex) => {
try {
const doc = await (tx || db.replicaNode())(TableName.Membership)
.join(TableName.Organization, `${TableName.Organization}.id`, `${TableName.Membership}.scopeOrgId`)
.where({ status: OrgMembershipStatus.Accepted, scope: AccessScope.Organization })
.andWhere((bd) => {
if (orgId) {
@@ -18,6 +19,7 @@ export const licenseDALFactory = (db: TDbClient) => {
})
.join(TableName.Users, `${TableName.Membership}.actorUserId`, `${TableName.Users}.id`)
.where(`${TableName.Users}.isGhost`, false)
.whereNull(`${TableName.Organization}.rootOrgId`)
.count();
return Number(doc?.[0]?.count ?? 0);
} catch (error) {
@@ -25,10 +27,31 @@ export const licenseDALFactory = (db: TDbClient) => {
}
};
const countOfOrgIdentities = async (orgId: string | null, tx?: Knex) => {
try {
// count org identities
const identityDoc = await (tx || db.replicaNode())(TableName.Identity)
.join(TableName.Organization, `${TableName.Identity}.orgId`, `${TableName.Organization}.id`)
.where((bd) => {
if (orgId) {
void bd.where(`${TableName.Organization}.rootOrgId`, orgId).orWhere(`${TableName.Organization}.id`, orgId);
}
})
.count();
const identityCount = Number(identityDoc?.[0].count);
return identityCount;
} catch (error) {
throw new DatabaseError({ error, name: "Count of Org Users + Identities" });
}
};
const countOrgUsersAndIdentities = async (orgId: string | null, tx?: Knex) => {
try {
// count org users
const userDoc = await (tx || db.replicaNode())(TableName.Membership)
.join(TableName.Organization, `${TableName.Organization}.id`, `${TableName.Membership}.scopeOrgId`)
.where({ status: OrgMembershipStatus.Accepted, scope: AccessScope.Organization })
.whereNotNull(`${TableName.Membership}.actorUserId`)
.andWhere((bd) => {
@@ -38,17 +61,17 @@ export const licenseDALFactory = (db: TDbClient) => {
})
.join(TableName.Users, `${TableName.Membership}.actorUserId`, `${TableName.Users}.id`)
.where(`${TableName.Users}.isGhost`, false)
.whereNull(`${TableName.Organization}.rootOrgId`)
.count();
const userCount = Number(userDoc?.[0].count);
// count org identities
const identityDoc = await (tx || db.replicaNode())(TableName.Membership)
.where({ scope: AccessScope.Organization })
.whereNotNull(`${TableName.Membership}.actorIdentityId`)
const identityDoc = await (tx || db.replicaNode())(TableName.Identity)
.join(TableName.Organization, `${TableName.Identity}.orgId`, `${TableName.Organization}.id`)
.where((bd) => {
if (orgId) {
void bd.where(`${TableName.Membership}.scopeOrgId`, orgId);
void bd.where(`${TableName.Organization}.rootOrgId`, orgId).orWhere(`${TableName.Organization}.id`, orgId);
}
})
.count();
@@ -61,5 +84,5 @@ export const licenseDALFactory = (db: TDbClient) => {
}
};
return { countOfOrgMembers, countOrgUsersAndIdentities };
return { countOfOrgMembers, countOrgUsersAndIdentities, countOfOrgIdentities };
};
@@ -28,6 +28,7 @@ export const getDefaultOnPremFeatures = (): TFeatureSet => ({
rbac: false,
githubOrgSync: false,
customRateLimits: false,
subOrganization: false,
customAlerts: false,
secretAccessInsights: false,
auditLogs: false,
@@ -9,12 +9,12 @@ import { AxiosError } from "axios";
import { CronJob } from "cron";
import { Knex } from "knex";
import { OrganizationActionScope } from "@app/db/schemas";
import { TKeyStoreFactory } from "@app/keystore/keystore";
import { TEnvConfig } from "@app/lib/config/env";
import { verifyOfflineLicense } from "@app/lib/crypto";
import { BadRequestError, NotFoundError } from "@app/lib/errors";
import { logger } from "@app/lib/logger";
import { TIdentityOrgDALFactory } from "@app/services/identity/identity-org-dal";
import { TOrgDALFactory } from "@app/services/org/org-dal";
import { TProjectDALFactory } from "@app/services/project/project-dal";
@@ -49,11 +49,10 @@ type TLicenseServiceFactoryDep = {
TEnvConfig,
"LICENSE_SERVER_URL" | "LICENSE_SERVER_KEY" | "LICENSE_KEY" | "LICENSE_KEY_OFFLINE" | "INTERNAL_REGION" | "SITE_URL"
>;
orgDAL: Pick<TOrgDALFactory, "findOrgById" | "countAllOrgMembers">;
orgDAL: Pick<TOrgDALFactory, "findRootOrgDetails" | "countAllOrgMembers" | "findById">;
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
licenseDAL: TLicenseDALFactory;
keyStore: Pick<TKeyStoreFactory, "setItemWithExpiry" | "getItem" | "deleteItem">;
identityOrgMembershipDAL: TIdentityOrgDALFactory;
projectDAL: TProjectDALFactory;
};
@@ -70,7 +69,6 @@ export const licenseServiceFactory = ({
permissionService,
licenseDAL,
keyStore,
identityOrgMembershipDAL,
projectDAL,
envConfig
}: TLicenseServiceFactoryDep) => {
@@ -203,19 +201,21 @@ export const licenseServiceFactory = ({
return JSON.parse(cachedPlan) as TFeatureSet;
}
const org = await orgDAL.findOrgById(orgId);
const org = await orgDAL.findRootOrgDetails(orgId);
if (!org) throw new NotFoundError({ message: `Organization with ID '${orgId}' not found` });
const rootOrgId = org.id;
const {
data: { currentPlan }
} = await licenseServerCloudApi.request.get<{ currentPlan: TFeatureSet }>(
`/api/license-server/v1/customers/${org.customerId}/cloud-plan`
);
const workspacesUsed = await projectDAL.countOfOrgProjects(orgId);
const workspacesUsed = await projectDAL.countOfOrgProjects(rootOrgId);
currentPlan.workspacesUsed = workspacesUsed;
const membersUsed = await licenseDAL.countOfOrgMembers(orgId);
const membersUsed = await licenseDAL.countOfOrgMembers(rootOrgId);
currentPlan.membersUsed = membersUsed;
const identityUsed = await licenseDAL.countOrgUsersAndIdentities(orgId);
const identityUsed = await licenseDAL.countOrgUsersAndIdentities(rootOrgId);
currentPlan.identitiesUsed = identityUsed;
if (currentPlan.identityLimit && currentPlan.identityLimit !== identityUsed) {
@@ -288,19 +288,20 @@ export const licenseServiceFactory = ({
};
const updateSubscriptionOrgMemberCount = async (orgId: string, tx?: Knex) => {
if (instanceType === InstanceType.Cloud) {
const org = await orgDAL.findOrgById(orgId);
if (!org) throw new NotFoundError({ message: `Organization with ID '${orgId}' not found` });
const org = await orgDAL.findRootOrgDetails(orgId, tx);
if (!org) throw new NotFoundError({ message: `Organization with ID '${orgId}' not found` });
const quantity = await licenseDAL.countOfOrgMembers(orgId, tx);
const quantityIdentities = await licenseDAL.countOrgUsersAndIdentities(orgId, tx);
const rootOrgId = org.id;
if (instanceType === InstanceType.Cloud) {
const quantity = await licenseDAL.countOfOrgMembers(rootOrgId, tx);
const quantityIdentities = await licenseDAL.countOrgUsersAndIdentities(rootOrgId, tx);
if (org?.customerId) {
await licenseServerCloudApi.request.patch(`/api/license-server/v1/customers/${org.customerId}/cloud-plan`, {
quantity,
quantityIdentities
});
}
await keyStore.deleteItem(FEATURE_CACHE_KEY(orgId));
await keyStore.deleteItem(FEATURE_CACHE_KEY(rootOrgId));
} else if (instanceType === InstanceType.EnterpriseOnPrem) {
const usedSeats = await licenseDAL.countOfOrgMembers(null, tx);
const usedIdentitySeats = await licenseDAL.countOrgUsersAndIdentities(null, tx);
@@ -311,7 +312,7 @@ export const licenseServiceFactory = ({
usedIdentitySeats
});
}
await refreshPlan(orgId);
await refreshPlan(rootOrgId);
};
// below all are api calls
@@ -323,7 +324,14 @@ export const licenseServiceFactory = ({
actorAuthMethod,
billingCycle
}: TOrgPlansTableDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
const { permission } = await permissionService.getOrgPermission({
actorId,
actor,
orgId,
actorOrgId,
actorAuthMethod,
scope: OrganizationActionScope.ParentOrganization
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionBillingActions.Read, OrgPermissionSubjects.Billing);
const { data } = await licenseServerCloudApi.request.get(
`/api/license-server/v1/cloud-products?billing-cycle=${billingCycle}`
@@ -340,7 +348,14 @@ export const licenseServiceFactory = ({
projectId,
refreshCache
}: TOrgPlanDTO) => {
await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
await permissionService.getOrgPermission({
actorId,
actor,
orgId,
actorOrgId,
actorAuthMethod,
scope: OrganizationActionScope.ParentOrganization
});
if (refreshCache) {
await refreshPlan(orgId);
}
@@ -356,13 +371,20 @@ export const licenseServiceFactory = ({
actorAuthMethod,
success_url
}: TStartOrgTrialDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
const { permission } = await permissionService.getOrgPermission({
actorId,
actor,
orgId,
actorOrgId,
actorAuthMethod,
scope: OrganizationActionScope.ParentOrganization
});
ForbiddenError.from(permission).throwUnlessCan(
OrgPermissionBillingActions.ManageBilling,
OrgPermissionSubjects.Billing
);
const organization = await orgDAL.findOrgById(orgId);
const organization = await orgDAL.findById(orgId);
if (!organization) {
throw new NotFoundError({
message: `Organization with ID '${orgId}' not found`
@@ -388,13 +410,20 @@ export const licenseServiceFactory = ({
actorAuthMethod,
actorOrgId
}: TCreateOrgPortalSession) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
const { permission } = await permissionService.getOrgPermission({
actorId,
actor,
orgId,
actorOrgId,
actorAuthMethod,
scope: OrganizationActionScope.ParentOrganization
});
ForbiddenError.from(permission).throwUnlessCan(
OrgPermissionBillingActions.ManageBilling,
OrgPermissionSubjects.Billing
);
const organization = await orgDAL.findOrgById(orgId);
const organization = await orgDAL.findById(orgId);
if (!organization) {
throw new NotFoundError({
message: "Organization not found"
@@ -437,10 +466,17 @@ export const licenseServiceFactory = ({
};
const getOrgBillingInfo = async ({ orgId, actor, actorId, actorAuthMethod, actorOrgId }: TGetOrgBillInfoDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
const { permission } = await permissionService.getOrgPermission({
actorId,
actor,
orgId,
actorOrgId,
actorAuthMethod,
scope: OrganizationActionScope.ParentOrganization
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionBillingActions.Read, OrgPermissionSubjects.Billing);
const organization = await orgDAL.findOrgById(orgId);
const organization = await orgDAL.findById(orgId);
if (!organization) {
throw new NotFoundError({
message: `Organization with ID '${orgId}' not found`
@@ -506,7 +542,7 @@ export const licenseServiceFactory = ({
const getUsageMetrics = async (orgId: string) => {
const [orgMembersUsed, identityUsed, projectCount] = await Promise.all([
orgDAL.countAllOrgMembers(orgId),
identityOrgMembershipDAL.countAllOrgIdentities({ scopeOrgId: orgId }),
licenseDAL.countOfOrgIdentities(orgId),
projectDAL.countOfOrgProjects(orgId)
]);
@@ -520,10 +556,17 @@ export const licenseServiceFactory = ({
// returns org current plan feature table
const getOrgPlanTable = async ({ orgId, actor, actorId, actorAuthMethod, actorOrgId }: TGetOrgBillInfoDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
const { permission } = await permissionService.getOrgPermission({
actorId,
actor,
orgId,
actorOrgId,
actorAuthMethod,
scope: OrganizationActionScope.ParentOrganization
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionBillingActions.Read, OrgPermissionSubjects.Billing);
const organization = await orgDAL.findOrgById(orgId);
const organization = await orgDAL.findById(orgId);
if (!organization) {
throw new NotFoundError({
message: `Organization with ID '${orgId}' not found`
@@ -557,10 +600,17 @@ export const licenseServiceFactory = ({
};
const getOrgBillingDetails = async ({ orgId, actor, actorId, actorAuthMethod, actorOrgId }: TGetOrgBillInfoDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
const { permission } = await permissionService.getOrgPermission({
actorId,
actor,
orgId,
actorOrgId,
actorAuthMethod,
scope: OrganizationActionScope.ParentOrganization
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionBillingActions.Read, OrgPermissionSubjects.Billing);
const organization = await orgDAL.findOrgById(orgId);
const organization = await orgDAL.findById(orgId);
if (!organization) {
throw new NotFoundError({
message: `Organization with ID '${orgId}' not found`
@@ -582,13 +632,20 @@ export const licenseServiceFactory = ({
name,
email
}: TUpdateOrgBillingDetailsDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
const { permission } = await permissionService.getOrgPermission({
actorId,
actor,
orgId,
actorOrgId,
actorAuthMethod,
scope: OrganizationActionScope.ParentOrganization
});
ForbiddenError.from(permission).throwUnlessCan(
OrgPermissionBillingActions.ManageBilling,
OrgPermissionSubjects.Billing
);
const organization = await orgDAL.findOrgById(orgId);
const organization = await orgDAL.findById(orgId);
if (!organization) {
throw new NotFoundError({
message: `Organization with ID '${orgId}' not found`
@@ -605,10 +662,17 @@ export const licenseServiceFactory = ({
};
const getOrgPmtMethods = async ({ orgId, actor, actorId, actorAuthMethod, actorOrgId }: TOrgPmtMethodsDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
const { permission } = await permissionService.getOrgPermission({
actorId,
actor,
orgId,
actorOrgId,
actorAuthMethod,
scope: OrganizationActionScope.ParentOrganization
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionBillingActions.Read, OrgPermissionSubjects.Billing);
const organization = await orgDAL.findOrgById(orgId);
const organization = await orgDAL.findById(orgId);
if (!organization) {
throw new NotFoundError({
message: `Organization with ID '${orgId}' not found`
@@ -632,13 +696,20 @@ export const licenseServiceFactory = ({
success_url,
cancel_url
}: TAddOrgPmtMethodDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
const { permission } = await permissionService.getOrgPermission({
actorId,
actor,
orgId,
actorOrgId,
actorAuthMethod,
scope: OrganizationActionScope.ParentOrganization
});
ForbiddenError.from(permission).throwUnlessCan(
OrgPermissionBillingActions.ManageBilling,
OrgPermissionSubjects.Billing
);
const organization = await orgDAL.findOrgById(orgId);
const organization = await orgDAL.findById(orgId);
if (!organization) {
throw new NotFoundError({
message: `Organization with ID '${orgId}' not found`
@@ -664,13 +735,20 @@ export const licenseServiceFactory = ({
orgId,
pmtMethodId
}: TDelOrgPmtMethodDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
const { permission } = await permissionService.getOrgPermission({
actorId,
actor,
orgId,
actorOrgId,
actorAuthMethod,
scope: OrganizationActionScope.ParentOrganization
});
ForbiddenError.from(permission).throwUnlessCan(
OrgPermissionBillingActions.ManageBilling,
OrgPermissionSubjects.Billing
);
const organization = await orgDAL.findOrgById(orgId);
const organization = await orgDAL.findById(orgId);
if (!organization) {
throw new NotFoundError({
message: `Organization with ID '${orgId}' not found`
@@ -696,10 +774,17 @@ export const licenseServiceFactory = ({
};
const getOrgTaxIds = async ({ orgId, actor, actorId, actorAuthMethod, actorOrgId }: TGetOrgTaxIdDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
const { permission } = await permissionService.getOrgPermission({
actorId,
actor,
orgId,
actorOrgId,
actorAuthMethod,
scope: OrganizationActionScope.ParentOrganization
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionBillingActions.Read, OrgPermissionSubjects.Billing);
const organization = await orgDAL.findOrgById(orgId);
const organization = await orgDAL.findById(orgId);
if (!organization) {
throw new NotFoundError({
message: `Organization with ID '${orgId}' not found`
@@ -714,13 +799,20 @@ export const licenseServiceFactory = ({
};
const addOrgTaxId = async ({ actorId, actor, actorAuthMethod, actorOrgId, orgId, type, value }: TAddOrgTaxIdDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
const { permission } = await permissionService.getOrgPermission({
actorId,
actor,
orgId,
actorOrgId,
actorAuthMethod,
scope: OrganizationActionScope.ParentOrganization
});
ForbiddenError.from(permission).throwUnlessCan(
OrgPermissionBillingActions.ManageBilling,
OrgPermissionSubjects.Billing
);
const organization = await orgDAL.findOrgById(orgId);
const organization = await orgDAL.findById(orgId);
if (!organization) {
throw new NotFoundError({
message: `Organization with ID '${orgId}' not found`
@@ -738,13 +830,20 @@ export const licenseServiceFactory = ({
};
const delOrgTaxId = async ({ orgId, actor, actorId, actorAuthMethod, actorOrgId, taxId }: TDelOrgTaxIdDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
const { permission } = await permissionService.getOrgPermission({
actorId,
actor,
orgId,
actorOrgId,
actorAuthMethod,
scope: OrganizationActionScope.ParentOrganization
});
ForbiddenError.from(permission).throwUnlessCan(
OrgPermissionBillingActions.ManageBilling,
OrgPermissionSubjects.Billing
);
const organization = await orgDAL.findOrgById(orgId);
const organization = await orgDAL.findById(orgId);
if (!organization) {
throw new NotFoundError({
message: `Organization with ID '${orgId}' not found`
@@ -758,10 +857,17 @@ export const licenseServiceFactory = ({
};
const getOrgTaxInvoices = async ({ actorId, actor, actorOrgId, actorAuthMethod, orgId }: TOrgInvoiceDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
const { permission } = await permissionService.getOrgPermission({
actorId,
actor,
orgId,
actorOrgId,
actorAuthMethod,
scope: OrganizationActionScope.ParentOrganization
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionBillingActions.Read, OrgPermissionSubjects.Billing);
const organization = await orgDAL.findOrgById(orgId);
const organization = await orgDAL.findById(orgId);
if (!organization) {
throw new NotFoundError({
message: `Organization with ID '${orgId}' not found`
@@ -775,10 +881,17 @@ export const licenseServiceFactory = ({
};
const getOrgLicenses = async ({ orgId, actor, actorId, actorAuthMethod, actorOrgId }: TOrgLicensesDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
const { permission } = await permissionService.getOrgPermission({
actorId,
actor,
orgId,
actorOrgId,
actorAuthMethod,
scope: OrganizationActionScope.ParentOrganization
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionBillingActions.Read, OrgPermissionSubjects.Billing);
const organization = await orgDAL.findOrgById(orgId);
const organization = await orgDAL.findById(orgId);
if (!organization) {
throw new NotFoundError({
message: `Organization with ID '${orgId}' not found`
@@ -823,7 +936,6 @@ export const licenseServiceFactory = ({
getLicenseId,
invalidateGetPlan,
updateSubscriptionOrgMemberCount,
refreshPlan,
getOrgPlan,
getOrgPlansTableByBillCycle,
startOrgTrial,
@@ -33,6 +33,7 @@ export type TFeatureSet = {
membersUsed: number;
identityLimit: null;
identitiesUsed: number;
subOrganization: false;
environmentLimit: null;
environmentsUsed: 0;
secretVersioning: true;
@@ -2,7 +2,7 @@
import { ForbiddenError } from "@casl/ability";
import { Issuer, Issuer as OpenIdIssuer, Strategy as OpenIdStrategy, TokenSet } from "openid-client";
import { AccessScope, OrgMembershipStatus, TableName, TUsers } from "@app/db/schemas";
import { AccessScope, OrganizationActionScope, OrgMembershipStatus, TableName, TUsers } from "@app/db/schemas";
import { TOidcConfigsUpdate } from "@app/db/schemas/oidc-configs";
import { EventType, TAuditLogServiceFactory } from "@app/ee/services/audit-log/audit-log-types";
import { TGroupDALFactory } from "@app/ee/services/group/group-dal";
@@ -118,13 +118,14 @@ export const oidcConfigServiceFactory = ({
}
if (dto.type === "external") {
const { permission } = await permissionService.getOrgPermission(
dto.actor,
dto.actorId,
dto.organizationId,
dto.actorAuthMethod,
dto.actorOrgId
);
const { permission } = await permissionService.getOrgPermission({
actorId: dto.actorId,
actor: dto.actor,
orgId: dto.organizationId,
actorOrgId: dto.actorOrgId,
actorAuthMethod: dto.actorAuthMethod,
scope: OrganizationActionScope.ParentOrganization
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Sso);
}
@@ -508,13 +509,14 @@ export const oidcConfigServiceFactory = ({
"Failed to update OIDC SSO configuration due to plan restriction. Upgrade plan to update SSO configuration."
});
const { permission } = await permissionService.getOrgPermission(
actor,
const { permission } = await permissionService.getOrgPermission({
actorId,
org.id,
actor,
orgId: org.id,
actorOrgId,
actorAuthMethod,
actorOrgId
);
scope: OrganizationActionScope.ParentOrganization
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Sso);
if (org.googleSsoAuthEnforced && isActive) {
@@ -602,13 +604,14 @@ export const oidcConfigServiceFactory = ({
"Failed to create OIDC SSO configuration due to plan restriction. Upgrade plan to update SSO configuration."
});
const { permission } = await permissionService.getOrgPermission(
actor,
const { permission } = await permissionService.getOrgPermission({
actorId,
org.id,
actor,
orgId: org.id,
actorOrgId,
actorAuthMethod,
actorOrgId
);
scope: OrganizationActionScope.ParentOrganization
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Sso);
if (org.googleSsoAuthEnforced && isActive) {
@@ -764,7 +767,14 @@ export const oidcConfigServiceFactory = ({
};
const isOidcManageGroupMembershipsEnabled = async (orgId: string, actor: OrgServiceActor) => {
await permissionService.getOrgPermission(ActorType.USER, actor.id, orgId, actor.authMethod, actor.orgId);
await permissionService.getOrgPermission({
actor: ActorType.USER,
actorId: actor.id,
orgId,
actorAuthMethod: actor.authMethod,
actorOrgId: actor.orgId,
scope: OrganizationActionScope.ParentOrganization
});
const oidcConfig = await oidcConfigDAL.findOne({
orgId,
@@ -1,6 +1,6 @@
import { ForbiddenError, subject } from "@casl/ability";
import { ActionProjectType, TPamAccounts, TPamResources } from "@app/db/schemas";
import { ActionProjectType, OrganizationActionScope, TPamAccounts, TPamResources } from "@app/db/schemas";
import { PAM_RESOURCE_FACTORY_MAP } from "@app/ee/services/pam-resource/pam-resource-factory";
import { decryptResource, decryptResourceConnectionDetails } from "@app/ee/services/pam-resource/pam-resource-fns";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
@@ -459,13 +459,14 @@ export const pamAccountServiceFactory = ({
const project = await projectDAL.findById(session.projectId);
if (!project) throw new NotFoundError({ message: `Project with ID '${session.projectId}' not found` });
const { permission } = await permissionService.getOrgPermission(
actor.type,
actor.id,
project.orgId,
actor.authMethod,
actor.orgId
);
const { permission } = await permissionService.getOrgPermission({
actor: actor.type,
actorId: actor.id,
orgId: project.orgId,
actorAuthMethod: actor.authMethod,
actorOrgId: actor.orgId,
scope: OrganizationActionScope.Any
});
ForbiddenError.from(permission).throwUnlessCan(
OrgPermissionGatewayActions.CreateGateways,
@@ -1,6 +1,6 @@
import { ForbiddenError } from "@casl/ability";
import { ActionProjectType } from "@app/db/schemas";
import { ActionProjectType, OrganizationActionScope } from "@app/db/schemas";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
import { OrgServiceActor } from "@app/lib/types";
@@ -102,13 +102,14 @@ export const pamSessionServiceFactory = ({
const project = await projectDAL.findById(session.projectId);
if (!project) throw new NotFoundError({ message: `Project with ID '${session.projectId}' not found` });
const { permission } = await permissionService.getOrgPermission(
actor.type,
actor.id,
project.orgId,
actor.authMethod,
actor.orgId
);
const { permission } = await permissionService.getOrgPermission({
actor: actor.type,
actorId: actor.id,
orgId: project.orgId,
actorAuthMethod: actor.authMethod,
actorOrgId: actor.orgId,
scope: OrganizationActionScope.Any
});
ForbiddenError.from(permission).throwUnlessCan(
OrgPermissionGatewayActions.CreateGateways,
@@ -142,13 +143,14 @@ export const pamSessionServiceFactory = ({
const project = await projectDAL.findById(session.projectId);
if (!project) throw new NotFoundError({ message: `Project with ID '${session.projectId}' not found` });
const { permission } = await permissionService.getOrgPermission(
actor.type,
actor.id,
project.orgId,
actor.authMethod,
actor.orgId
);
const { permission } = await permissionService.getOrgPermission({
actor: actor.type,
actorId: actor.id,
orgId: project.orgId,
actorAuthMethod: actor.authMethod,
actorOrgId: actor.orgId,
scope: OrganizationActionScope.Any
});
if (actor.type === ActorType.IDENTITY) {
ForbiddenError.from(permission).throwUnlessCan(
@@ -15,6 +15,11 @@ export enum OrgPermissionActions {
Delete = "delete"
}
export enum OrgPermissionSubOrgActions {
Create = "create",
DirectAccess = "direct-access"
}
export enum OrgPermissionAppConnectionActions {
Read = "read",
Create = "create",
@@ -117,7 +122,8 @@ export enum OrgPermissionSubjects {
Kmip = "kmip",
Gateway = "gateway",
Relay = "relay",
SecretShare = "secret-share"
SecretShare = "secret-share",
SubOrganization = "sub-organization"
}
export type AppConnectionSubjectFields = {
@@ -128,6 +134,7 @@ export type OrgPermissionSet =
| [OrgPermissionActions.Create, OrgPermissionSubjects.Workspace]
| [OrgPermissionActions.Create, OrgPermissionSubjects.Project]
| [OrgPermissionActions, OrgPermissionSubjects.Role]
| [OrgPermissionSubOrgActions, OrgPermissionSubjects.SubOrganization]
| [OrgPermissionActions, OrgPermissionSubjects.Member]
| [OrgPermissionActions, OrgPermissionSubjects.Settings]
| [OrgPermissionActions, OrgPermissionSubjects.IncidentAccount]
@@ -185,6 +192,12 @@ export const OrgPermissionSchema = z.discriminatedUnion("subject", [
subject: z.literal(OrgPermissionSubjects.Role).describe("The entity this permission pertains to."),
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(OrgPermissionActions).describe("Describe what action an entity can take.")
}),
z.object({
subject: z.literal(OrgPermissionSubjects.SubOrganization).describe("The entity this permission pertains to."),
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(OrgPermissionSubOrgActions).describe(
"Describe what action an entity can take."
)
}),
z.object({
subject: z.literal(OrgPermissionSubjects.Member).describe("The entity this permission pertains to."),
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(OrgPermissionActions).describe("Describe what action an entity can take.")
@@ -308,6 +321,10 @@ const buildAdminPermission = () => {
// ws permissions
can(OrgPermissionActions.Create, OrgPermissionSubjects.Workspace);
can(OrgPermissionActions.Create, OrgPermissionSubjects.Project);
can(OrgPermissionSubOrgActions.Create, OrgPermissionSubjects.SubOrganization);
can(OrgPermissionSubOrgActions.DirectAccess, OrgPermissionSubjects.SubOrganization);
// role permission
can(OrgPermissionActions.Read, OrgPermissionSubjects.Role);
can(OrgPermissionActions.Create, OrgPermissionSubjects.Role);
@@ -19,6 +19,7 @@ interface TPermissionDataReturn extends TMemberships {
orgAuthEnforced?: boolean | null;
orgGoogleSsoAuthEnforced?: boolean | null;
shouldUseNewPrivilegeSystem?: boolean | null;
rootOrgId?: string | null;
bypassOrgAuthEnabled?: boolean | null;
roles: {
id: string;
@@ -273,7 +274,8 @@ export const permissionDALFactory = (db: TDbClient): TPermissionDALFactory => {
db.ref("shouldUseNewPrivilegeSystem").withSchema(TableName.Organization),
db.ref("authEnforced").withSchema(TableName.Organization).as("orgAuthEnforced"),
db.ref("googleSsoAuthEnforced").withSchema(TableName.Organization).as("orgGoogleSsoAuthEnforced"),
db.ref("bypassOrgAuthEnabled").withSchema(TableName.Organization).as("bypassOrgAuthEnabled")
db.ref("bypassOrgAuthEnabled").withSchema(TableName.Organization).as("bypassOrgAuthEnabled"),
db.ref("rootOrgId").withSchema(TableName.Organization).as("rootOrgId")
);
const data = sqlNestRelationships({
@@ -283,6 +285,7 @@ export const permissionDALFactory = (db: TDbClient): TPermissionDALFactory => {
MembershipsSchema.extend({
orgAuthEnforced: z.boolean().optional().nullable(),
shouldUseNewPrivilegeSystem: z.boolean().optional().nullable(),
rootOrgId: z.string().optional().nullable(),
orgGoogleSsoAuthEnforced: z.boolean(),
bypassOrgAuthEnabled: z.boolean()
}).parse(el),
@@ -2,7 +2,7 @@ import { MongoAbility } from "@casl/ability";
import { MongoQuery } from "@ucast/mongo2js";
import { Knex } from "knex";
import { ActionProjectType, TMemberships } from "@app/db/schemas";
import { ActionProjectType, OrganizationActionScope, TMemberships } from "@app/db/schemas";
import { ActorAuthMethod, ActorType } from "@app/services/auth/auth-type";
import { OrgPermissionSet } from "./org-permission";
@@ -18,21 +18,6 @@ export type TBuildOrgPermissionDTO = {
role: string;
}[];
export type TGetUserProjectPermissionArg = {
userId: string;
projectId: string;
authMethod: ActorAuthMethod;
actionProjectType: ActionProjectType;
userOrgId?: string;
};
export type TGetIdentityProjectPermissionArg = {
identityId: string;
projectId: string;
identityOrgId?: string;
actionProjectType: ActionProjectType;
};
export type TGetServiceTokenProjectPermissionArg = {
serviceTokenId: string;
projectId: string;
@@ -54,17 +39,12 @@ export type TGetOrgPermissionArg = {
actorId: string;
orgId: string;
actorAuthMethod: ActorAuthMethod;
actorOrgId?: string;
actorOrgId: string;
scope: OrganizationActionScope;
};
export type TPermissionServiceFactory = {
getOrgPermission: (
type: ActorType,
id: string,
orgId: string,
authMethod: ActorAuthMethod,
actorOrgId: string | undefined
) => Promise<{
getOrgPermission: (arg: TGetOrgPermissionArg) => Promise<{
permission: MongoAbility<OrgPermissionSet, MongoQuery>;
memberships: Array<
TMemberships & {
@@ -7,6 +7,7 @@ import { Knex } from "knex";
import {
AccessScope,
ActionProjectType,
OrganizationActionScope,
OrgMembershipRole,
ProjectMembershipRole,
ServiceTokenScopes
@@ -179,14 +180,15 @@ export const permissionServiceFactory = ({
// return minTtl;
// };
const getOrgPermission: TPermissionServiceFactory["getOrgPermission"] = async (
type,
id,
const getOrgPermission: TPermissionServiceFactory["getOrgPermission"] = async ({
actor,
actorId,
orgId,
authMethod,
actorOrgId
) => {
if (type !== ActorType.USER && type !== ActorType.IDENTITY) {
actorOrgId,
scope,
actorAuthMethod
}) => {
if (actor !== ActorType.USER && actor !== ActorType.IDENTITY) {
throw new BadRequestError({
message: "Invalid actor provided",
name: "Get org permission"
@@ -202,11 +204,19 @@ export const permissionServiceFactory = ({
scope: AccessScope.Organization,
orgId
},
actorId: id,
actorType: type
actorId,
actorType: actor
});
if (!permissionData?.length) throw new ForbiddenRequestError({ name: "You are not member of this organization" });
const rootOrgId = permissionData?.[0]?.rootOrgId;
const isChild = Boolean(rootOrgId);
if (scope === OrganizationActionScope.ParentOrganization && isChild) {
throw new ForbiddenRequestError({ message: `Child organization cannot do this operation` });
} else if (scope === OrganizationActionScope.ChildOrganization && !isChild) {
throw new ForbiddenRequestError({ message: `Parent organization cannot do this operation` });
}
const permissionFromRoles = permissionData.flatMap((membership) => {
const activeRoles = membership?.roles
.filter(
@@ -227,7 +237,7 @@ export const permissionServiceFactory = ({
permissionData.some((memberships) => memberships.roles.some((el) => role === (el.customRoleSlug || el.role)));
validateOrgSSO(
authMethod,
actorAuthMethod,
permissionData?.[0].orgAuthEnforced,
Boolean(permissionData?.[0].orgGoogleSsoAuthEnforced),
Boolean(permissionData?.[0].bypassOrgAuthEnabled),
@@ -1,7 +1,7 @@
import { ForbiddenError } from "@casl/ability";
import { packRules } from "@casl/ability/extra";
import { ProjectType, TProjectTemplates } from "@app/db/schemas";
import { OrganizationActionScope, ProjectType, TProjectTemplates } from "@app/db/schemas";
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
@@ -59,13 +59,14 @@ export const projectTemplateServiceFactory = ({
message: "Failed to access project templates due to plan restriction. Upgrade plan to access project templates."
});
const { permission } = await permissionService.getOrgPermission(
actor.type,
actor.id,
actor.orgId,
actor.authMethod,
actor.orgId
);
const { permission } = await permissionService.getOrgPermission({
actor: actor.type,
actorId: actor.id,
orgId: actor.orgId,
actorAuthMethod: actor.authMethod,
actorOrgId: actor.orgId,
scope: OrganizationActionScope.Any
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.ProjectTemplates);
@@ -97,13 +98,14 @@ export const projectTemplateServiceFactory = ({
if (!projectTemplate) throw new NotFoundError({ message: `Could not find project template with Name "${name}"` });
const { permission } = await permissionService.getOrgPermission(
actor.type,
actor.id,
projectTemplate.orgId,
actor.authMethod,
actor.orgId
);
const { permission } = await permissionService.getOrgPermission({
actor: actor.type,
actorId: actor.id,
orgId: projectTemplate.orgId,
actorAuthMethod: actor.authMethod,
actorOrgId: actor.orgId,
scope: OrganizationActionScope.Any
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.ProjectTemplates);
@@ -125,13 +127,14 @@ export const projectTemplateServiceFactory = ({
if (!projectTemplate) throw new NotFoundError({ message: `Could not find project template with ID ${id}` });
const { permission } = await permissionService.getOrgPermission(
actor.type,
actor.id,
projectTemplate.orgId,
actor.authMethod,
actor.orgId
);
const { permission } = await permissionService.getOrgPermission({
actor: actor.type,
actorId: actor.id,
orgId: projectTemplate.orgId,
actorAuthMethod: actor.authMethod,
actorOrgId: actor.orgId,
scope: OrganizationActionScope.Any
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.ProjectTemplates);
@@ -152,13 +155,14 @@ export const projectTemplateServiceFactory = ({
message: "Failed to create project template due to plan restriction. Upgrade plan to access project templates."
});
const { permission } = await permissionService.getOrgPermission(
actor.type,
actor.id,
actor.orgId,
actor.authMethod,
actor.orgId
);
const { permission } = await permissionService.getOrgPermission({
actor: actor.type,
actorId: actor.id,
orgId: actor.orgId,
actorAuthMethod: actor.authMethod,
actorOrgId: actor.orgId,
scope: OrganizationActionScope.Any
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.ProjectTemplates);
@@ -213,13 +217,14 @@ export const projectTemplateServiceFactory = ({
if (!projectTemplate) throw new NotFoundError({ message: `Could not find project template with ID ${id}` });
const { permission } = await permissionService.getOrgPermission(
actor.type,
actor.id,
projectTemplate.orgId,
actor.authMethod,
actor.orgId
);
const { permission } = await permissionService.getOrgPermission({
actor: actor.type,
actorId: actor.id,
orgId: projectTemplate.orgId,
actorAuthMethod: actor.authMethod,
actorOrgId: actor.orgId,
scope: OrganizationActionScope.Any
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.ProjectTemplates);
if (projectTemplate.type !== ProjectType.SecretManager && environments)
@@ -272,13 +277,14 @@ export const projectTemplateServiceFactory = ({
if (!projectTemplate) throw new NotFoundError({ message: `Could not find project template with ID ${id}` });
const { permission } = await permissionService.getOrgPermission(
actor.type,
actor.id,
projectTemplate.orgId,
actor.authMethod,
actor.orgId
);
const { permission } = await permissionService.getOrgPermission({
actor: actor.type,
actorId: actor.id,
orgId: projectTemplate.orgId,
actorAuthMethod: actor.authMethod,
actorOrgId: actor.orgId,
scope: OrganizationActionScope.Any
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Delete, OrgPermissionSubjects.ProjectTemplates);
@@ -2,7 +2,7 @@ import { z } from "zod";
import { ProjectMembershipRole, ProjectType, TProjectEnvironments } from "@app/db/schemas";
import { TProjectPermissionV2Schema } from "@app/ee/services/permission/project-permission";
import { OrgServiceActor } from "@app/lib/types";
import { ProjectServiceActor } from "@app/lib/types";
import { UnpackedPermissionSchema } from "@app/server/routes/sanitizedSchema/permission";
export type TProjectTemplateEnvironment = Pick<TProjectEnvironments, "name" | "slug" | "position">;
@@ -31,7 +31,7 @@ export enum InfisicalProjectTemplate {
export type TProjectTemplateServiceFactory = {
listProjectTemplatesByOrg: (
actor: OrgServiceActor,
actor: ProjectServiceActor,
type?: ProjectType
) => Promise<
(
@@ -85,7 +85,7 @@ export type TProjectTemplateServiceFactory = {
>;
createProjectTemplate: (
arg: TCreateProjectTemplateDTO,
actor: OrgServiceActor
actor: ProjectServiceActor
) => Promise<{
environments: TProjectTemplateEnvironment[];
roles: {
@@ -109,7 +109,7 @@ export type TProjectTemplateServiceFactory = {
updateProjectTemplateById: (
id: string,
{ roles, environments, ...params }: TUpdateProjectTemplateDTO,
actor: OrgServiceActor
actor: ProjectServiceActor
) => Promise<{
environments: TProjectTemplateEnvironment[];
roles: {
@@ -132,7 +132,7 @@ export type TProjectTemplateServiceFactory = {
}>;
deleteProjectTemplateById: (
id: string,
actor: OrgServiceActor
actor: ProjectServiceActor
) => Promise<{
environments: TProjectTemplateEnvironment[];
roles: {
@@ -155,7 +155,7 @@ export type TProjectTemplateServiceFactory = {
}>;
findProjectTemplateById: (
id: string,
actor: OrgServiceActor
actor: ProjectServiceActor
) => Promise<{
packedRoles: TProjectTemplateRole[];
environments: TProjectTemplateEnvironment[];
@@ -179,7 +179,7 @@ export type TProjectTemplateServiceFactory = {
}>;
findProjectTemplateByName: (
name: string,
actor: OrgServiceActor
actor: ProjectServiceActor
) => Promise<{
packedRoles: TProjectTemplateRole[];
environments: TProjectTemplateEnvironment[];
+25 -21
View File
@@ -3,7 +3,7 @@ import { isIP } from "node:net";
import { ForbiddenError } from "@casl/ability";
import * as x509 from "@peculiar/x509";
import { OrgMembershipRole, TRelays } from "@app/db/schemas";
import { OrganizationActionScope, OrgMembershipRole, TRelays } from "@app/db/schemas";
import { PgSqlLock } from "@app/keystore/keystore";
import { crypto } from "@app/lib/crypto";
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
@@ -126,8 +126,8 @@ export const relayServiceFactory = ({
// generate instance relay CA
const instanceRelayCaSerialNumber = createSerialNumber();
const instanceRelayCaIssuedAt = new Date();
const instanceRelayCaExpiration = new Date(new Date().setFullYear(2045));
const instanceRelayCaIssuedAt = new Date();
const instanceRelayCaKeys = await crypto.nativeCrypto.subtle.generateKey(alg, true, ["sign", "verify"]);
const instanceRelayCaSkObj = crypto.nativeCrypto.KeyObject.from(instanceRelayCaKeys.privateKey);
const instanceRelayCaCert = await x509.X509CertificateGenerator.create({
@@ -972,13 +972,14 @@ export const relayServiceFactory = ({
});
}
const { permission } = await permissionService.getOrgPermission(
ActorType.IDENTITY,
identityId,
const { permission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.Any,
actor: ActorType.IDENTITY,
actorId: identityId,
orgId,
actorAuthMethod!,
orgId
);
actorAuthMethod: actorAuthMethod!,
actorOrgId: orgId
});
ForbiddenError.from(permission).throwUnlessCan(
OrgPermissionRelayActions.CreateRelays,
@@ -1102,13 +1103,14 @@ export const relayServiceFactory = ({
});
}
const { permission } = await permissionService.getOrgPermission(
ActorType.IDENTITY,
identityId,
const { permission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.Any,
actor: ActorType.IDENTITY,
actorId: identityId,
orgId,
actorAuthMethod!,
orgId
);
actorAuthMethod: actorAuthMethod!,
actorOrgId: orgId
});
ForbiddenError.from(permission).throwUnlessCan(
OrgPermissionRelayActions.CreateRelays,
OrgPermissionSubjects.Relay
@@ -1155,13 +1157,14 @@ export const relayServiceFactory = ({
actorAuthMethod: ActorAuthMethod;
actorOrgId: string;
}) => {
const { permission } = await permissionService.getOrgPermission(
const { permission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.Any,
actor,
actorId,
actorOrgId,
actorAuthMethod,
orgId: actorOrgId,
actorAuthMethod: actorAuthMethod!,
actorOrgId
);
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionRelayActions.ListRelays, OrgPermissionSubjects.Relay);
@@ -1189,13 +1192,14 @@ export const relayServiceFactory = ({
actorAuthMethod: ActorAuthMethod;
actorOrgId: string;
}) => {
const { permission } = await permissionService.getOrgPermission(
const { permission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.Any,
actor,
actorId,
actorOrgId,
orgId: actorOrgId,
actorAuthMethod,
actorOrgId
);
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionRelayActions.DeleteRelays, OrgPermissionSubjects.Relay);
@@ -5,6 +5,7 @@ import RE2 from "re2";
import {
AccessScope,
OrganizationActionScope,
OrgMembershipRole,
OrgMembershipStatus,
TableName,
@@ -251,7 +252,14 @@ export const samlConfigServiceFactory = ({
authProvider,
enableGroupSync
}) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
const { permission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.ParentOrganization,
actor,
actorId,
orgId,
actorAuthMethod,
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Sso);
const plan = await licenseService.getPlan(orgId);
@@ -317,7 +325,14 @@ export const samlConfigServiceFactory = ({
authProvider,
enableGroupSync
}) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
const { permission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.ParentOrganization,
actor,
actorId,
orgId,
actorAuthMethod,
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Sso);
const plan = await licenseService.getPlan(orgId);
if (!plan.samlSSO)
@@ -393,7 +408,7 @@ export const samlConfigServiceFactory = ({
});
}
} else if (dto.type === "orgSlug") {
const org = await orgDAL.findOne({ slug: dto.orgSlug });
const org = await orgDAL.findOne({ slug: dto.orgSlug, rootOrgId: null });
if (!org) {
throw new NotFoundError({
message: `Organization with slug '${dto.orgSlug}' not found`
@@ -424,13 +439,14 @@ export const samlConfigServiceFactory = ({
// when dto is type id means it's internally used
if (dto.type === "org") {
const { permission } = await permissionService.getOrgPermission(
dto.actor,
dto.actorId,
samlConfig.orgId,
dto.actorAuthMethod,
dto.actorOrgId
);
const { permission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.ParentOrganization,
actor: dto.actor,
actorId: dto.actorId,
orgId: samlConfig.orgId,
actorAuthMethod: dto.actorAuthMethod,
actorOrgId: dto.actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Sso);
}
const { decryptor } = await kmsService.createCipherPairWithDataKey({
@@ -37,7 +37,7 @@ export type TGetSamlCfgDTO =
actor: ActorType;
actorId: string;
actorAuthMethod: ActorAuthMethod;
actorOrgId: string | undefined;
actorOrgId: string;
}
| {
type: "orgSlug";
+22 -5
View File
@@ -4,6 +4,7 @@ import { scimPatch } from "scim-patch";
import {
AccessScope,
OrganizationActionScope,
OrgMembershipRole,
OrgMembershipStatus,
TableName,
@@ -56,6 +57,7 @@ type TScimServiceFactoryDep = {
TOrgDALFactory,
| "createMembership"
| "findById"
| "find"
| "findMembership"
| "findMembershipWithScimFilter"
| "deleteMembershipById"
@@ -125,7 +127,14 @@ export const scimServiceFactory = ({
description,
ttlDays
}) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
const { permission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.ParentOrganization,
actor,
actorId,
orgId,
actorAuthMethod,
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Scim);
const plan = await licenseService.getPlan(orgId);
@@ -160,7 +169,14 @@ export const scimServiceFactory = ({
actorAuthMethod,
orgId
}) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
const { permission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.ParentOrganization,
actor,
actorId,
orgId,
actorAuthMethod,
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Scim);
const plan = await licenseService.getPlan(orgId);
@@ -183,13 +199,14 @@ export const scimServiceFactory = ({
let scimToken = await scimDAL.findById(scimTokenId);
if (!scimToken) throw new NotFoundError({ message: `SCIM token with ID '${scimTokenId}' not found` });
const { permission } = await permissionService.getOrgPermission(
const { permission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.ParentOrganization,
actor,
actorId,
scimToken.orgId,
orgId: scimToken.orgId,
actorAuthMethod,
actorOrgId
);
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Delete, OrgPermissionSubjects.Scim);
const plan = await licenseService.getPlan(scimToken.orgId);
@@ -2,6 +2,7 @@ import { ForbiddenError } from "@casl/ability";
import { WebhookEventMap } from "@octokit/webhooks-types";
import { ProbotOctokit } from "probot";
import { OrganizationActionScope } from "@app/db/schemas";
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
import { getConfig } from "@app/lib/config/env";
@@ -49,7 +50,14 @@ export const secretScanningServiceFactory = ({
}: TInstallAppSessionDTO) => {
const appCfg = getConfig();
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
const { permission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.Any,
actor,
actorId,
orgId,
actorAuthMethod,
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.SecretScanning);
const sessionId = crypto.randomBytes(16).toString("hex");
@@ -68,13 +76,14 @@ export const secretScanningServiceFactory = ({
const session = await gitAppInstallSessionDAL.findOne({ sessionId });
if (!session) throw new NotFoundError({ message: "Session was not found" });
const { permission } = await permissionService.getOrgPermission(
const { permission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.Any,
actor,
actorId,
session.orgId,
orgId: session.orgId,
actorAuthMethod,
actorOrgId
);
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.SecretScanning);
const installatedApp = await gitAppOrgDAL.transaction(async (tx) => {
await gitAppInstallSessionDAL.deleteById(session.id, tx);
@@ -117,7 +126,14 @@ export const secretScanningServiceFactory = ({
actorAuthMethod,
actorOrgId
}: TGetOrgInstallStatusDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
const { permission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.Any,
actor,
actorId,
orgId,
actorAuthMethod,
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.SecretScanning);
const appInstallation = await gitAppOrgDAL.findOne({ orgId });
@@ -125,7 +141,14 @@ export const secretScanningServiceFactory = ({
};
const getRisksByOrg = async ({ actor, orgId, actorId, actorAuthMethod, actorOrgId, filter }: TGetOrgRisksDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
const { permission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.Any,
actor,
actorId,
orgId,
actorAuthMethod,
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.SecretScanning);
const results = await secretScanningDAL.findByOrgId(orgId, filter);
@@ -134,7 +157,14 @@ export const secretScanningServiceFactory = ({
};
const getAllRisksByOrg = async ({ actor, orgId, actorId, actorAuthMethod, actorOrgId }: TGetAllOrgRisksDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
const { permission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.Any,
actor,
actorId,
orgId,
actorAuthMethod,
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.SecretScanning);
const risks = await secretScanningDAL.find({ orgId }, { sort: [["createdAt", "desc"]] });
@@ -150,7 +180,14 @@ export const secretScanningServiceFactory = ({
riskId,
status
}: TUpdateRiskStatusDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
const { permission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.Any,
actor,
actorId,
orgId,
actorAuthMethod,
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.SecretScanning);
const isRiskResolved = Boolean(
@@ -0,0 +1,160 @@
import { ForbiddenError } from "@casl/ability";
import { AccessScope, OrganizationActionScope, OrgMembershipRole, OrgMembershipStatus } from "@app/db/schemas";
import { BadRequestError } from "@app/lib/errors";
import { ActorType } from "@app/services/auth/auth-type";
import { TMembershipDALFactory } from "@app/services/membership/membership-dal";
import { TMembershipRoleDALFactory } from "@app/services/membership/membership-role-dal";
import { TOrgDALFactory } from "@app/services/org/org-dal";
import { TLicenseServiceFactory } from "../license/license-service";
import { OrgPermissionActions, OrgPermissionSubjects, OrgPermissionSubOrgActions } from "../permission/org-permission";
import { TPermissionServiceFactory } from "../permission/permission-service-types";
import { TCreateSubOrgDTO, TListSubOrgDTO, TUpdateSubOrgDTO } from "./sub-org-types";
type TSubOrgServiceFactoryDep = {
orgDAL: Pick<
TOrgDALFactory,
"findOne" | "create" | "transaction" | "listSubOrganizations" | "updateById" | "findById"
>;
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
membershipDAL: Pick<TMembershipDALFactory, "create">;
membershipRoleDAL: Pick<TMembershipRoleDALFactory, "create">;
};
export type TSubOrgServiceFactory = ReturnType<typeof subOrgServiceFactory>;
export const subOrgServiceFactory = ({
orgDAL,
permissionService,
licenseService,
membershipDAL,
membershipRoleDAL
}: TSubOrgServiceFactoryDep) => {
const createSubOrg = async ({ name, permissionActor }: TCreateSubOrgDTO) => {
const { permission } = await permissionService.getOrgPermission({
actorId: permissionActor.id,
actor: permissionActor.type,
orgId: permissionActor.orgId,
actorOrgId: permissionActor.orgId,
actorAuthMethod: permissionActor.authMethod,
scope: OrganizationActionScope.ParentOrganization
});
ForbiddenError.from(permission).throwUnlessCan(
OrgPermissionSubOrgActions.Create,
OrgPermissionSubjects.SubOrganization
);
const orgLicensePlan = await licenseService.getPlan(permissionActor.rootOrgId);
if (!orgLicensePlan.subOrganization) {
throw new BadRequestError({
message: "Sub-organization creation failed. Please upgrade your instance to Infisical's Enterprise plan."
});
}
const existingSubOrg = await orgDAL.findOne({
parentOrgId: permissionActor.orgId,
name
});
if (existingSubOrg) {
throw new BadRequestError({ message: `Sub-organization with name ${name} already exists` });
}
const organization = await orgDAL.transaction(async (tx) => {
const org = await orgDAL.create(
{ name, slug: name, rootOrgId: permissionActor.rootOrgId, parentOrgId: permissionActor.orgId },
tx
);
const membership = await membershipDAL.create(
{
scope: AccessScope.Organization,
[permissionActor.type === ActorType.IDENTITY ? "actorIdentityId" : "actorUserId"]: permissionActor.id,
scopeOrgId: org.id,
status: OrgMembershipStatus.Accepted,
isActive: true
},
tx
);
await membershipRoleDAL.create(
{
membershipId: membership.id,
role: OrgMembershipRole.Admin
},
tx
);
return org;
});
return {
organization
};
};
const listSubOrgs = async ({ permissionActor, data }: TListSubOrgDTO) => {
await permissionService.getOrgPermission({
actorId: permissionActor.id,
actor: permissionActor.type,
orgId: permissionActor.rootOrgId,
actorOrgId: permissionActor.rootOrgId,
actorAuthMethod: permissionActor.authMethod,
scope: OrganizationActionScope.Any
});
const organizations = await orgDAL.listSubOrganizations({
actorId: permissionActor.id,
actorType: permissionActor.type,
orgId: permissionActor.rootOrgId,
isAccessible: data?.isAccessible,
limit: data?.limit,
offset: data?.offset
});
return {
organizations
};
};
const updateSubOrg = async ({ subOrgId, name, permissionActor }: TUpdateSubOrgDTO) => {
const subOrg = await orgDAL.findOne({
rootOrgId: permissionActor.rootOrgId,
id: subOrgId
});
if (!subOrg) {
throw new BadRequestError({ message: "Sub-organization not found" });
}
const { permission } = await permissionService.getOrgPermission({
actorId: permissionActor.id,
actor: permissionActor.type,
orgId: subOrgId,
actorOrgId: subOrgId,
actorAuthMethod: permissionActor.authMethod,
scope: OrganizationActionScope.ChildOrganization
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Settings);
const existingSubOrg = await orgDAL.findOne({
parentOrgId: subOrg.parentOrgId,
slug: name
});
if (existingSubOrg && existingSubOrg.id !== subOrgId) {
throw new BadRequestError({ message: `Sub-organization with name ${name} already exists` });
}
const organization = await orgDAL.updateById(subOrgId, { name, slug: name });
return {
organization
};
};
return {
createSubOrg,
listSubOrgs,
updateSubOrg
};
};
@@ -0,0 +1,22 @@
import { OrgServiceActor } from "@app/lib/types";
export type TCreateSubOrgDTO = {
name: string;
permissionActor: OrgServiceActor;
};
export type TListSubOrgDTO = {
permissionActor: OrgServiceActor;
data: Partial<{
limit?: number;
offset?: number;
search?: string;
isAccessible?: boolean;
}>;
};
export type TUpdateSubOrgDTO = {
subOrgId: string;
name: string;
permissionActor: OrgServiceActor;
};