mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-09 08:28:22 +00:00
Merge pull request #4701 from Infisical/feat/sub-org
feat: sub organization
This commit is contained in:
Vendored
+4
@@ -48,6 +48,7 @@ import { TSshCertificateAuthorityServiceFactory } from "@app/ee/services/ssh/ssh
|
||||
import { TSshCertificateTemplateServiceFactory } from "@app/ee/services/ssh-certificate-template/ssh-certificate-template-service";
|
||||
import { TSshHostServiceFactory } from "@app/ee/services/ssh-host/ssh-host-service";
|
||||
import { TSshHostGroupServiceFactory } from "@app/ee/services/ssh-host-group/ssh-host-group-service";
|
||||
import { TSubOrgServiceFactory } from "@app/ee/services/sub-org/sub-org-service";
|
||||
import { TTrustedIpServiceFactory } from "@app/ee/services/trusted-ip/trusted-ip-types";
|
||||
import { TAuthMode } from "@app/server/plugins/auth/inject-identity";
|
||||
import { TAdditionalPrivilegeServiceFactory } from "@app/services/additional-privilege/additional-privilege-service";
|
||||
@@ -182,6 +183,8 @@ declare module "fastify" {
|
||||
type: ActorType;
|
||||
id: string;
|
||||
orgId: string;
|
||||
parentOrgId: string;
|
||||
rootOrgId: string;
|
||||
};
|
||||
rateLimits: RateLimitConfiguration;
|
||||
// passport data
|
||||
@@ -335,6 +338,7 @@ declare module "fastify" {
|
||||
additionalPrivilege: TAdditionalPrivilegeServiceFactory;
|
||||
role: TRoleServiceFactory;
|
||||
convertor: TConvertorServiceFactory;
|
||||
subOrganization: TSubOrgServiceFactory;
|
||||
};
|
||||
// this is exclusive use for middlewares in which we need to inject data
|
||||
// everywhere else access using service layer
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
import { dropConstraintIfExists } from "@app/db/migrations/utils/dropConstraintIfExists";
|
||||
|
||||
import { AccessScope, TableName } from "../schemas";
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
const hasParentOrgId = await knex.schema.hasColumn(TableName.Organization, "parentOrgId");
|
||||
if (!hasParentOrgId) {
|
||||
await knex.schema.alterTable(TableName.Organization, async (t) => {
|
||||
// the one just above the chain
|
||||
t.uuid("parentOrgId");
|
||||
t.foreign("parentOrgId").references("id").inTable(TableName.Organization).onDelete("CASCADE");
|
||||
// this would root organization containing various informations like billing etc
|
||||
t.uuid("rootOrgId");
|
||||
t.foreign("rootOrgId").references("id").inTable(TableName.Organization).onDelete("CASCADE");
|
||||
|
||||
await dropConstraintIfExists(TableName.Organization, "organizations_slug_unique", knex);
|
||||
t.unique(["rootOrgId", "parentOrgId", "slug"]);
|
||||
});
|
||||
|
||||
// had to switch to raw for null not distinct
|
||||
}
|
||||
|
||||
const hasIdentityOrgCol = await knex.schema.hasColumn(TableName.Identity, "orgId");
|
||||
if (!hasIdentityOrgCol) {
|
||||
await knex.schema.alterTable(TableName.Identity, (t) => {
|
||||
t.uuid("orgId");
|
||||
t.foreign("orgId").references("id").inTable(TableName.Organization).onDelete("CASCADE");
|
||||
});
|
||||
|
||||
await knex.raw(
|
||||
`
|
||||
UPDATE ?? AS identity
|
||||
SET "orgId" = membership."scopeOrgId"
|
||||
FROM ?? AS membership
|
||||
WHERE
|
||||
membership."actorIdentityId" = identity."id"
|
||||
AND membership."scope" = ?
|
||||
`,
|
||||
[TableName.Identity, TableName.Membership, AccessScope.Organization]
|
||||
);
|
||||
|
||||
await knex.schema.alterTable(TableName.Identity, (t) => {
|
||||
t.uuid("orgId").notNullable().alter();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
const hasParentOrgId = await knex.schema.hasColumn(TableName.Organization, "parentOrgId");
|
||||
const hasRootOrgId = await knex.schema.hasColumn(TableName.Organization, "rootOrgId");
|
||||
if (hasParentOrgId || hasRootOrgId) {
|
||||
await knex.schema.alterTable(TableName.Organization, (t) => {
|
||||
if (hasParentOrgId) t.dropColumn("parentOrgId");
|
||||
if (hasRootOrgId) t.dropColumn("rootOrgId");
|
||||
});
|
||||
}
|
||||
|
||||
const hasIdentityOrgCol = await knex.schema.hasColumn(TableName.Identity, "orgId");
|
||||
if (hasIdentityOrgCol) {
|
||||
await knex.schema.alterTable(TableName.Identity, (t) => {
|
||||
t.dropColumn("orgId");
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,8 @@ export const IdentitiesSchema = z.object({
|
||||
authMethod: z.string().nullable().optional(),
|
||||
createdAt: z.date(),
|
||||
updatedAt: z.date(),
|
||||
hasDeleteProtection: z.boolean().default(false)
|
||||
hasDeleteProtection: z.boolean().default(false),
|
||||
orgId: z.string().uuid()
|
||||
});
|
||||
|
||||
export type TIdentities = z.infer<typeof IdentitiesSchema>;
|
||||
|
||||
@@ -316,6 +316,12 @@ export enum ActionProjectType {
|
||||
Any = "any"
|
||||
}
|
||||
|
||||
export enum OrganizationActionScope {
|
||||
ChildOrganization = "child-organization-only",
|
||||
ParentOrganization = "parent-organization-only",
|
||||
Any = "any"
|
||||
}
|
||||
|
||||
export enum TemporaryPermissionMode {
|
||||
Relative = "relative"
|
||||
}
|
||||
|
||||
@@ -38,7 +38,9 @@ export const OrganizationsSchema = z.object({
|
||||
maxSharedSecretLifetime: z.number().default(2592000).nullable().optional(),
|
||||
maxSharedSecretViewLimit: z.number().nullable().optional(),
|
||||
googleSsoAuthEnforced: z.boolean().default(false),
|
||||
googleSsoAuthLastUsed: z.date().nullable().optional()
|
||||
googleSsoAuthLastUsed: z.date().nullable().optional(),
|
||||
parentOrgId: z.string().uuid().nullable().optional(),
|
||||
rootOrgId: z.string().uuid().nullable().optional()
|
||||
});
|
||||
|
||||
export type TOrganizations = z.infer<typeof OrganizationsSchema>;
|
||||
|
||||
@@ -24,7 +24,8 @@ export async function seed(knex: Knex): Promise<void> {
|
||||
// @ts-ignore
|
||||
id: seedData1.machineIdentity.id,
|
||||
name: seedData1.machineIdentity.name,
|
||||
authMethod: IdentityAuthMethod.UNIVERSAL_AUTH
|
||||
authMethod: IdentityAuthMethod.UNIVERSAL_AUTH,
|
||||
orgId: seedData1.organization.id
|
||||
}
|
||||
]);
|
||||
const identityUa = await knex(TableName.IdentityUniversalAuth)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 { getConfig } 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";
|
||||
|
||||
@@ -45,11 +45,10 @@ import {
|
||||
} from "./license-types";
|
||||
|
||||
type TLicenseServiceFactoryDep = {
|
||||
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;
|
||||
};
|
||||
|
||||
@@ -66,7 +65,6 @@ export const licenseServiceFactory = ({
|
||||
permissionService,
|
||||
licenseDAL,
|
||||
keyStore,
|
||||
identityOrgMembershipDAL,
|
||||
projectDAL
|
||||
}: TLicenseServiceFactoryDep) => {
|
||||
let isValidLicense = false;
|
||||
@@ -199,19 +197,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) {
|
||||
@@ -284,19 +284,20 @@ export const licenseServiceFactory = ({
|
||||
};
|
||||
|
||||
const updateSubscriptionOrgMemberCount = async (orgId: string, tx?: Knex) => {
|
||||
if (instanceType === InstanceType.Cloud) {
|
||||
const org = await orgDAL.findOrgById(orgId);
|
||||
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);
|
||||
@@ -307,7 +308,7 @@ export const licenseServiceFactory = ({
|
||||
usedIdentitySeats
|
||||
});
|
||||
}
|
||||
await refreshPlan(orgId);
|
||||
await refreshPlan(rootOrgId);
|
||||
};
|
||||
|
||||
// below all are api calls
|
||||
@@ -319,7 +320,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}`
|
||||
@@ -336,7 +344,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);
|
||||
}
|
||||
@@ -352,13 +367,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`
|
||||
@@ -384,13 +406,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"
|
||||
@@ -433,10 +462,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`
|
||||
@@ -502,7 +538,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)
|
||||
]);
|
||||
|
||||
@@ -516,10 +552,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`
|
||||
@@ -553,10 +596,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`
|
||||
@@ -578,13 +628,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`
|
||||
@@ -601,10 +658,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`
|
||||
@@ -628,13 +692,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`
|
||||
@@ -660,13 +731,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`
|
||||
@@ -692,10 +770,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`
|
||||
@@ -710,13 +795,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`
|
||||
@@ -734,13 +826,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`
|
||||
@@ -754,10 +853,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`
|
||||
@@ -771,10 +877,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`
|
||||
@@ -819,7 +932,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[];
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
@@ -33,6 +33,7 @@ export enum ApiDocsTags {
|
||||
LdapAuth = "LDAP Auth",
|
||||
Groups = "Groups",
|
||||
Organizations = "Organizations",
|
||||
SubOrganizations = "Sub Organizations",
|
||||
Projects = "Projects",
|
||||
ProjectUsers = "Project Users",
|
||||
ProjectGroups = "Project Groups",
|
||||
@@ -717,6 +718,21 @@ export const ORGANIZATIONS = {
|
||||
}
|
||||
} as const;
|
||||
|
||||
export const SUB_ORGANIZATIONS = {
|
||||
CREATE: {
|
||||
name: "The name of the sub organization to create."
|
||||
},
|
||||
UPDATE: {
|
||||
name: "The name of the sub organization to update.",
|
||||
subOrgId: "The id of the sub organization to update."
|
||||
},
|
||||
LIST: {
|
||||
limit: "The number of sub organizations to return.",
|
||||
offset: "The offset to start from. If you enter 10, it will start from the 10th sub organization.",
|
||||
isAccessible: "Filter to only return sub organizations that the actor has access to."
|
||||
}
|
||||
} as const;
|
||||
|
||||
export const PROJECTS = {
|
||||
CREATE: {
|
||||
organizationSlug: "The slug of the organization to create the project in.",
|
||||
|
||||
@@ -5,7 +5,7 @@ export type TGenericPermission = {
|
||||
actor: ActorType;
|
||||
actorId: string;
|
||||
actorAuthMethod: ActorAuthMethod;
|
||||
actorOrgId: string | undefined;
|
||||
actorOrgId: string;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -78,6 +78,15 @@ export type OrgServiceActor = {
|
||||
id: string;
|
||||
authMethod: ActorAuthMethod;
|
||||
orgId: string;
|
||||
rootOrgId: string;
|
||||
parentOrgId: string;
|
||||
};
|
||||
|
||||
export type ProjectServiceActor = {
|
||||
type: ActorType;
|
||||
id: string;
|
||||
authMethod: ActorAuthMethod;
|
||||
orgId: string;
|
||||
};
|
||||
|
||||
export enum QueueWorkerProfile {
|
||||
|
||||
@@ -8,6 +8,7 @@ import { TScimTokenJwtPayload } from "@app/ee/services/scim/scim-types";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { crypto } from "@app/lib/crypto";
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
import { slugSchema } from "@app/server/lib/schemas";
|
||||
import { ActorType, AuthMethod, AuthMode, AuthModeJwtTokenPayload, AuthTokenType } from "@app/services/auth/auth-type";
|
||||
import { TIdentityAccessTokenJwtPayload } from "@app/services/identity-access-token/identity-access-token-types";
|
||||
import { getServerCfg } from "@app/services/super-admin/super-admin-service";
|
||||
@@ -20,6 +21,8 @@ export type TAuthMode =
|
||||
tokenVersionId: string; // the session id of token used
|
||||
user: TUsers;
|
||||
orgId: string;
|
||||
rootOrgId: string;
|
||||
parentOrgId: string;
|
||||
authMethod: AuthMethod;
|
||||
isMfaVerified?: boolean;
|
||||
token: AuthModeJwtTokenPayload;
|
||||
@@ -31,6 +34,8 @@ export type TAuthMode =
|
||||
userId: string;
|
||||
user: TUsers;
|
||||
orgId: string;
|
||||
rootOrgId: string;
|
||||
parentOrgId: string;
|
||||
token: string;
|
||||
}
|
||||
| {
|
||||
@@ -39,6 +44,8 @@ export type TAuthMode =
|
||||
actor: ActorType.SERVICE;
|
||||
serviceTokenId: string;
|
||||
orgId: string;
|
||||
rootOrgId: string;
|
||||
parentOrgId: string;
|
||||
authMethod: null;
|
||||
token: string;
|
||||
}
|
||||
@@ -48,6 +55,8 @@ export type TAuthMode =
|
||||
identityId: string;
|
||||
identityName: string;
|
||||
orgId: string;
|
||||
rootOrgId: string;
|
||||
parentOrgId: string;
|
||||
authMethod: null;
|
||||
isInstanceAdmin?: boolean;
|
||||
token: TIdentityAccessTokenJwtPayload;
|
||||
@@ -57,6 +66,8 @@ export type TAuthMode =
|
||||
actor: ActorType.SCIM_CLIENT;
|
||||
scimTokenId: string;
|
||||
orgId: string;
|
||||
rootOrgId: string;
|
||||
parentOrgId: string;
|
||||
authMethod: null;
|
||||
};
|
||||
|
||||
@@ -136,17 +147,26 @@ export const injectIdentity = fp(
|
||||
|
||||
if (!authMode) return;
|
||||
|
||||
const subOrganizationSelector = req.headers?.["x-infisical-org"] as string | undefined;
|
||||
if (subOrganizationSelector) {
|
||||
await slugSchema().parseAsync(subOrganizationSelector);
|
||||
}
|
||||
|
||||
switch (authMode) {
|
||||
case AuthMode.JWT: {
|
||||
const { user, tokenVersionId, orgId } = await server.services.authToken.fnValidateJwtIdentity(token);
|
||||
const { user, tokenVersionId, orgId, rootOrgId, parentOrgId } =
|
||||
await server.services.authToken.fnValidateJwtIdentity(token, subOrganizationSelector);
|
||||
requestContext.set("orgId", orgId);
|
||||
|
||||
req.auth = {
|
||||
authMode: AuthMode.JWT,
|
||||
user,
|
||||
userId: user.id,
|
||||
tokenVersionId,
|
||||
actor,
|
||||
orgId: orgId as string,
|
||||
orgId,
|
||||
rootOrgId,
|
||||
parentOrgId,
|
||||
authMethod: token.authMethod,
|
||||
isMfaVerified: token.isMfaVerified,
|
||||
token
|
||||
@@ -154,13 +174,19 @@ export const injectIdentity = fp(
|
||||
break;
|
||||
}
|
||||
case AuthMode.IDENTITY_ACCESS_TOKEN: {
|
||||
const identity = await server.services.identityAccessToken.fnValidateIdentityAccessToken(token, req.realIp);
|
||||
const identity = await server.services.identityAccessToken.fnValidateIdentityAccessToken(
|
||||
token,
|
||||
subOrganizationSelector,
|
||||
req.realIp
|
||||
);
|
||||
const serverCfg = await getServerCfg();
|
||||
requestContext.set("orgId", identity.orgId);
|
||||
req.auth = {
|
||||
authMode: AuthMode.IDENTITY_ACCESS_TOKEN,
|
||||
actor,
|
||||
orgId: identity.orgId,
|
||||
rootOrgId: identity.rootOrgId,
|
||||
parentOrgId: identity.parentOrgId,
|
||||
identityId: identity.identityId,
|
||||
identityName: identity.name,
|
||||
authMethod: null,
|
||||
@@ -190,8 +216,14 @@ export const injectIdentity = fp(
|
||||
case AuthMode.SERVICE_TOKEN: {
|
||||
const serviceToken = await server.services.serviceToken.fnValidateServiceToken(token);
|
||||
requestContext.set("orgId", serviceToken.orgId);
|
||||
|
||||
if (subOrganizationSelector)
|
||||
throw new BadRequestError({ message: `Service token doesn't support sub organization selector` });
|
||||
|
||||
req.auth = {
|
||||
orgId: serviceToken.orgId,
|
||||
rootOrgId: serviceToken.rootOrgId,
|
||||
parentOrgId: serviceToken.parentOrgId,
|
||||
authMode: AuthMode.SERVICE_TOKEN as const,
|
||||
serviceToken,
|
||||
serviceTokenId: serviceToken.id,
|
||||
@@ -202,22 +234,27 @@ export const injectIdentity = fp(
|
||||
break;
|
||||
}
|
||||
case AuthMode.API_KEY: {
|
||||
const user = await server.services.apiKey.fnValidateApiKey(token as string);
|
||||
req.auth = {
|
||||
authMode: AuthMode.API_KEY as const,
|
||||
userId: user.id,
|
||||
actor,
|
||||
user,
|
||||
orgId: "API_KEY", // We set the orgId to an arbitrary value, since we can't link an API key to a specific org. We have to deprecate API keys soon!
|
||||
authMethod: null,
|
||||
token: token as string
|
||||
};
|
||||
break;
|
||||
throw new BadRequestError({
|
||||
message: "API key authentication is not supported anymore. Please switch to identity authentication."
|
||||
});
|
||||
}
|
||||
case AuthMode.SCIM_TOKEN: {
|
||||
const { orgId, scimTokenId } = await server.services.scim.fnValidateScimToken(token);
|
||||
requestContext.set("orgId", orgId);
|
||||
req.auth = { authMode: AuthMode.SCIM_TOKEN, actor, scimTokenId, orgId, authMethod: null };
|
||||
|
||||
if (subOrganizationSelector)
|
||||
throw new BadRequestError({ message: `SCIM token doesn't support sub organization selector` });
|
||||
|
||||
req.auth = {
|
||||
authMode: AuthMode.SCIM_TOKEN,
|
||||
actor,
|
||||
scimTokenId,
|
||||
orgId,
|
||||
authMethod: null,
|
||||
// scim cannot be done for sub organization
|
||||
rootOrgId: orgId,
|
||||
parentOrgId: orgId
|
||||
};
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
||||
@@ -14,7 +14,9 @@ export const injectPermission = fp(async (server) => {
|
||||
type: ActorType.USER,
|
||||
id: req.auth.userId,
|
||||
orgId: req.auth.orgId, // if the req.auth.authMode is AuthMode.API_KEY, the orgId will be "API_KEY"
|
||||
authMethod: req.auth.authMethod // if the req.auth.authMode is AuthMode.API_KEY, the authMethod will be null
|
||||
authMethod: req.auth.authMethod, // if the req.auth.authMode is AuthMode.API_KEY, the authMethod will be null
|
||||
rootOrgId: req.auth.rootOrgId,
|
||||
parentOrgId: req.auth.parentOrgId
|
||||
};
|
||||
|
||||
logger.info(
|
||||
@@ -25,7 +27,9 @@ export const injectPermission = fp(async (server) => {
|
||||
type: ActorType.IDENTITY,
|
||||
id: req.auth.identityId,
|
||||
orgId: req.auth.orgId,
|
||||
authMethod: null
|
||||
authMethod: null,
|
||||
rootOrgId: req.auth.rootOrgId,
|
||||
parentOrgId: req.auth.parentOrgId
|
||||
};
|
||||
|
||||
logger.info(
|
||||
@@ -36,6 +40,8 @@ export const injectPermission = fp(async (server) => {
|
||||
type: ActorType.SERVICE,
|
||||
id: req.auth.serviceTokenId,
|
||||
orgId: req.auth.orgId,
|
||||
rootOrgId: req.auth.rootOrgId,
|
||||
parentOrgId: req.auth.parentOrgId,
|
||||
authMethod: null
|
||||
};
|
||||
|
||||
@@ -47,6 +53,8 @@ export const injectPermission = fp(async (server) => {
|
||||
type: ActorType.SCIM_CLIENT,
|
||||
id: req.auth.scimTokenId,
|
||||
orgId: req.auth.orgId,
|
||||
rootOrgId: req.auth.rootOrgId,
|
||||
parentOrgId: req.auth.parentOrgId,
|
||||
authMethod: null
|
||||
};
|
||||
|
||||
|
||||
@@ -131,6 +131,7 @@ import { sshHostLoginUserDALFactory } from "@app/ee/services/ssh-host/ssh-login-
|
||||
import { sshHostGroupDALFactory } from "@app/ee/services/ssh-host-group/ssh-host-group-dal";
|
||||
import { sshHostGroupMembershipDALFactory } from "@app/ee/services/ssh-host-group/ssh-host-group-membership-dal";
|
||||
import { sshHostGroupServiceFactory } from "@app/ee/services/ssh-host-group/ssh-host-group-service";
|
||||
import { subOrgServiceFactory } from "@app/ee/services/sub-org/sub-org-service";
|
||||
import { trustedIpDALFactory } from "@app/ee/services/trusted-ip/trusted-ip-dal";
|
||||
import { trustedIpServiceFactory } from "@app/ee/services/trusted-ip/trusted-ip-service";
|
||||
import { keyValueStoreDALFactory } from "@app/keystore/key-value-store-dal";
|
||||
@@ -568,11 +569,10 @@ export const registerRoutes = async (
|
||||
orgDAL,
|
||||
licenseDAL,
|
||||
keyStore,
|
||||
identityOrgMembershipDAL,
|
||||
projectDAL
|
||||
});
|
||||
|
||||
const tokenService = tokenServiceFactory({ tokenDAL: authTokenDAL, userDAL, membershipUserDAL });
|
||||
const tokenService = tokenServiceFactory({ tokenDAL: authTokenDAL, userDAL, membershipUserDAL, orgDAL });
|
||||
|
||||
const membershipUserService = membershipUserServiceFactory({
|
||||
licenseService,
|
||||
@@ -592,6 +592,7 @@ export const registerRoutes = async (
|
||||
});
|
||||
|
||||
const membershipIdentityService = membershipIdentityServiceFactory({
|
||||
identityDAL,
|
||||
membershipIdentityDAL,
|
||||
membershipRoleDAL,
|
||||
orgDAL,
|
||||
@@ -912,6 +913,15 @@ export const registerRoutes = async (
|
||||
userGroupMembershipDAL,
|
||||
additionalPrivilegeDAL
|
||||
});
|
||||
|
||||
const subOrgService = subOrgServiceFactory({
|
||||
licenseService,
|
||||
membershipDAL,
|
||||
membershipRoleDAL,
|
||||
orgDAL,
|
||||
permissionService
|
||||
});
|
||||
|
||||
const signupService = authSignupServiceFactory({
|
||||
tokenService,
|
||||
smtpService,
|
||||
@@ -1594,10 +1604,12 @@ export const registerRoutes = async (
|
||||
permissionService,
|
||||
projectDAL,
|
||||
accessTokenQueue,
|
||||
smtpService
|
||||
smtpService,
|
||||
orgDAL
|
||||
});
|
||||
|
||||
const identityService = identityServiceFactory({
|
||||
additionalPrivilegeDAL,
|
||||
permissionService,
|
||||
identityDAL,
|
||||
identityOrgMembershipDAL,
|
||||
@@ -1628,10 +1640,12 @@ export const registerRoutes = async (
|
||||
identityAccessTokenDAL,
|
||||
accessTokenQueue,
|
||||
identityDAL,
|
||||
membershipIdentityDAL
|
||||
membershipIdentityDAL,
|
||||
orgDAL
|
||||
});
|
||||
|
||||
const identityTokenAuthService = identityTokenAuthServiceFactory({
|
||||
identityDAL,
|
||||
identityTokenAuthDAL,
|
||||
identityAccessTokenDAL,
|
||||
permissionService,
|
||||
@@ -1641,6 +1655,7 @@ export const registerRoutes = async (
|
||||
});
|
||||
|
||||
const identityUaService = identityUaServiceFactory({
|
||||
identityDAL,
|
||||
permissionService,
|
||||
identityAccessTokenDAL,
|
||||
identityUaClientSecretDAL,
|
||||
@@ -1652,6 +1667,7 @@ export const registerRoutes = async (
|
||||
});
|
||||
|
||||
const identityKubernetesAuthService = identityKubernetesAuthServiceFactory({
|
||||
identityDAL,
|
||||
identityKubernetesAuthDAL,
|
||||
identityAccessTokenDAL,
|
||||
permissionService,
|
||||
@@ -1665,6 +1681,7 @@ export const registerRoutes = async (
|
||||
membershipIdentityDAL
|
||||
});
|
||||
const identityGcpAuthService = identityGcpAuthServiceFactory({
|
||||
identityDAL,
|
||||
identityGcpAuthDAL,
|
||||
orgDAL,
|
||||
identityAccessTokenDAL,
|
||||
@@ -1674,6 +1691,7 @@ export const registerRoutes = async (
|
||||
});
|
||||
|
||||
const identityAliCloudAuthService = identityAliCloudAuthServiceFactory({
|
||||
identityDAL,
|
||||
identityAccessTokenDAL,
|
||||
orgDAL,
|
||||
identityAliCloudAuthDAL,
|
||||
@@ -1683,6 +1701,7 @@ export const registerRoutes = async (
|
||||
});
|
||||
|
||||
const identityTlsCertAuthService = identityTlsCertAuthServiceFactory({
|
||||
identityDAL,
|
||||
identityAccessTokenDAL,
|
||||
identityTlsCertAuthDAL,
|
||||
licenseService,
|
||||
@@ -1692,6 +1711,7 @@ export const registerRoutes = async (
|
||||
});
|
||||
|
||||
const identityAwsAuthService = identityAwsAuthServiceFactory({
|
||||
identityDAL,
|
||||
identityAccessTokenDAL,
|
||||
orgDAL,
|
||||
identityAwsAuthDAL,
|
||||
@@ -1701,6 +1721,7 @@ export const registerRoutes = async (
|
||||
});
|
||||
|
||||
const identityAzureAuthService = identityAzureAuthServiceFactory({
|
||||
identityDAL,
|
||||
identityAzureAuthDAL,
|
||||
orgDAL,
|
||||
identityAccessTokenDAL,
|
||||
@@ -1710,6 +1731,7 @@ export const registerRoutes = async (
|
||||
});
|
||||
|
||||
const identityOciAuthService = identityOciAuthServiceFactory({
|
||||
identityDAL,
|
||||
identityAccessTokenDAL,
|
||||
orgDAL,
|
||||
identityOciAuthDAL,
|
||||
@@ -1733,6 +1755,7 @@ export const registerRoutes = async (
|
||||
});
|
||||
|
||||
const identityOidcAuthService = identityOidcAuthServiceFactory({
|
||||
identityDAL,
|
||||
identityOidcAuthDAL,
|
||||
orgDAL,
|
||||
identityAccessTokenDAL,
|
||||
@@ -1743,6 +1766,7 @@ export const registerRoutes = async (
|
||||
});
|
||||
|
||||
const identityJwtAuthService = identityJwtAuthServiceFactory({
|
||||
identityDAL,
|
||||
identityJwtAuthDAL,
|
||||
orgDAL,
|
||||
permissionService,
|
||||
@@ -2296,6 +2320,7 @@ export const registerRoutes = async (
|
||||
groupProject: groupProjectService,
|
||||
permission: permissionService,
|
||||
org: orgService,
|
||||
subOrganization: subOrgService,
|
||||
oidc: oidcService,
|
||||
apiKey: apiKeyService,
|
||||
authToken: tokenService,
|
||||
|
||||
@@ -94,6 +94,7 @@ export const registerAuthRoutes = async (server: FastifyZodProvider) => {
|
||||
decodedToken.userId,
|
||||
decodedToken.organizationId,
|
||||
decodedToken.authMethod,
|
||||
decodedToken.organizationId,
|
||||
decodedToken.organizationId
|
||||
);
|
||||
if (org && org.userTokenExpiration) {
|
||||
|
||||
@@ -73,12 +73,12 @@ export const registerIdentityAliCloudAuthRouter = async (server: FastifyZodProvi
|
||||
}
|
||||
},
|
||||
handler: async (req) => {
|
||||
const { identityAliCloudAuth, accessToken, identityAccessToken, identityMembershipOrg } =
|
||||
const { identityAliCloudAuth, accessToken, identityAccessToken, identity } =
|
||||
await server.services.identityAliCloudAuth.login(req.body);
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
orgId: identity.orgId,
|
||||
event: {
|
||||
type: EventType.LOGIN_IDENTITY_ALICLOUD_AUTH,
|
||||
metadata: {
|
||||
|
||||
@@ -40,12 +40,12 @@ export const registerIdentityAwsAuthRouter = async (server: FastifyZodProvider)
|
||||
}
|
||||
},
|
||||
handler: async (req) => {
|
||||
const { identityAwsAuth, accessToken, identityAccessToken, identityMembershipOrg } =
|
||||
const { identityAwsAuth, accessToken, identityAccessToken, identity } =
|
||||
await server.services.identityAwsAuth.login(req.body);
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
orgId: identity.orgId,
|
||||
event: {
|
||||
type: EventType.LOGIN_IDENTITY_AWS_AUTH,
|
||||
metadata: {
|
||||
|
||||
@@ -35,12 +35,12 @@ export const registerIdentityAzureAuthRouter = async (server: FastifyZodProvider
|
||||
}
|
||||
},
|
||||
handler: async (req) => {
|
||||
const { identityAzureAuth, accessToken, identityAccessToken, identityMembershipOrg } =
|
||||
const { identityAzureAuth, accessToken, identityAccessToken, identity } =
|
||||
await server.services.identityAzureAuth.login(req.body);
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
orgId: identity.orgId,
|
||||
event: {
|
||||
type: EventType.LOGIN_IDENTITY_AZURE_AUTH,
|
||||
metadata: {
|
||||
|
||||
@@ -35,12 +35,12 @@ export const registerIdentityGcpAuthRouter = async (server: FastifyZodProvider)
|
||||
}
|
||||
},
|
||||
handler: async (req) => {
|
||||
const { identityGcpAuth, accessToken, identityAccessToken, identityMembershipOrg } =
|
||||
const { identityGcpAuth, accessToken, identityAccessToken, identity } =
|
||||
await server.services.identityGcpAuth.login(req.body);
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
orgId: identity.orgId,
|
||||
event: {
|
||||
type: EventType.LOGIN_IDENTITY_GCP_AUTH,
|
||||
metadata: {
|
||||
|
||||
@@ -111,7 +111,7 @@ export const registerIdentityJwtAuthRouter = async (server: FastifyZodProvider)
|
||||
}
|
||||
},
|
||||
handler: async (req) => {
|
||||
const { identityJwtAuth, accessToken, identityAccessToken, identityMembershipOrg } =
|
||||
const { identityJwtAuth, accessToken, identityAccessToken, identity } =
|
||||
await server.services.identityJwtAuth.login({
|
||||
identityId: req.body.identityId,
|
||||
jwt: req.body.jwt
|
||||
@@ -119,7 +119,7 @@ export const registerIdentityJwtAuthRouter = async (server: FastifyZodProvider)
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
orgId: identity.orgId,
|
||||
event: {
|
||||
type: EventType.LOGIN_IDENTITY_JWT_AUTH,
|
||||
metadata: {
|
||||
|
||||
@@ -56,7 +56,7 @@ export const registerIdentityKubernetesRouter = async (server: FastifyZodProvide
|
||||
}
|
||||
},
|
||||
handler: async (req) => {
|
||||
const { identityKubernetesAuth, accessToken, identityAccessToken, identityMembershipOrg } =
|
||||
const { identityKubernetesAuth, accessToken, identityAccessToken, identity } =
|
||||
await server.services.identityKubernetesAuth.login({
|
||||
identityId: req.body.identityId,
|
||||
jwt: req.body.jwt
|
||||
@@ -64,7 +64,7 @@ export const registerIdentityKubernetesRouter = async (server: FastifyZodProvide
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
orgId: identity.orgId,
|
||||
event: {
|
||||
type: EventType.LOGIN_IDENTITY_KUBERNETES_AUTH,
|
||||
metadata: {
|
||||
|
||||
@@ -162,13 +162,13 @@ export const registerIdentityLdapAuthRouter = async (server: FastifyZodProvider)
|
||||
|
||||
const { identityId, user } = req.passportMachineIdentity;
|
||||
|
||||
const { accessToken, identityLdapAuth, identityMembershipOrg } = await server.services.identityLdapAuth.login({
|
||||
const { accessToken, identityLdapAuth, identity } = await server.services.identityLdapAuth.login({
|
||||
identityId
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
orgId: identity.orgId,
|
||||
event: {
|
||||
type: EventType.LOGIN_IDENTITY_LDAP_AUTH,
|
||||
metadata: {
|
||||
|
||||
@@ -52,12 +52,12 @@ export const registerIdentityOciAuthRouter = async (server: FastifyZodProvider)
|
||||
}
|
||||
},
|
||||
handler: async (req) => {
|
||||
const { identityOciAuth, accessToken, identityAccessToken, identityMembershipOrg } =
|
||||
const { identityOciAuth, accessToken, identityAccessToken, identity } =
|
||||
await server.services.identityOciAuth.login(req.body);
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
orgId: identity.orgId,
|
||||
event: {
|
||||
type: EventType.LOGIN_IDENTITY_OCI_AUTH,
|
||||
metadata: {
|
||||
|
||||
@@ -59,7 +59,7 @@ export const registerIdentityOidcAuthRouter = async (server: FastifyZodProvider)
|
||||
}
|
||||
},
|
||||
handler: async (req) => {
|
||||
const { identityOidcAuth, accessToken, identityAccessToken, identityMembershipOrg, oidcTokenData } =
|
||||
const { identityOidcAuth, accessToken, identityAccessToken, identity, oidcTokenData } =
|
||||
await server.services.identityOidcAuth.login({
|
||||
identityId: req.body.identityId,
|
||||
jwt: req.body.jwt
|
||||
@@ -67,7 +67,7 @@ export const registerIdentityOidcAuthRouter = async (server: FastifyZodProvider)
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
orgId: identity.orgId,
|
||||
event: {
|
||||
type: EventType.LOGIN_IDENTITY_OIDC_AUTH,
|
||||
metadata: {
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { AccessScope, TemporaryPermissionMode } from "@app/db/schemas";
|
||||
import { ApiDocsTags, PROJECT_IDENTITIES } from "@app/lib/api-docs";
|
||||
import { ms } from "@app/lib/ms";
|
||||
import { writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
const sanitizedOrgIdentityMembershipSchema = z.object({
|
||||
id: z.string().uuid(),
|
||||
orgId: z.string(),
|
||||
identityId: z.string().uuid(),
|
||||
createdAt: z.date(),
|
||||
updatedAt: z.date()
|
||||
});
|
||||
|
||||
export const registerOrgIdentityMembershipRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "POST",
|
||||
url: "/identity-memberships/:identityId",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
schema: {
|
||||
hide: true,
|
||||
// this is hidden so not updating tags
|
||||
tags: [ApiDocsTags.ProjectIdentities],
|
||||
description: "Create org identity membership",
|
||||
security: [
|
||||
{
|
||||
bearerAuth: []
|
||||
}
|
||||
],
|
||||
params: z.object({
|
||||
identityId: z.string().trim()
|
||||
}),
|
||||
body: z.object({
|
||||
roles: z
|
||||
.array(
|
||||
z.union([
|
||||
z.object({
|
||||
role: z.string().describe(PROJECT_IDENTITIES.CREATE_IDENTITY_MEMBERSHIP.roles.role),
|
||||
isTemporary: z
|
||||
.literal(false)
|
||||
.default(false)
|
||||
.describe(PROJECT_IDENTITIES.CREATE_IDENTITY_MEMBERSHIP.roles.role)
|
||||
}),
|
||||
z.object({
|
||||
role: z.string().describe(PROJECT_IDENTITIES.CREATE_IDENTITY_MEMBERSHIP.roles.role),
|
||||
isTemporary: z.literal(true).describe(PROJECT_IDENTITIES.CREATE_IDENTITY_MEMBERSHIP.roles.role),
|
||||
temporaryMode: z
|
||||
.nativeEnum(TemporaryPermissionMode)
|
||||
.describe(PROJECT_IDENTITIES.CREATE_IDENTITY_MEMBERSHIP.roles.role),
|
||||
temporaryRange: z
|
||||
.string()
|
||||
.refine((val) => ms(val) > 0, "Temporary range must be a positive number")
|
||||
.describe(PROJECT_IDENTITIES.CREATE_IDENTITY_MEMBERSHIP.roles.role),
|
||||
temporaryAccessStartTime: z
|
||||
.string()
|
||||
.datetime()
|
||||
.describe(PROJECT_IDENTITIES.CREATE_IDENTITY_MEMBERSHIP.roles.role)
|
||||
})
|
||||
])
|
||||
)
|
||||
.describe(PROJECT_IDENTITIES.CREATE_IDENTITY_MEMBERSHIP.roles.description)
|
||||
.max(1)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
identityMembership: sanitizedOrgIdentityMembershipSchema
|
||||
})
|
||||
}
|
||||
},
|
||||
handler: async (req) => {
|
||||
const { membership } = await server.services.membershipIdentity.createMembership({
|
||||
permission: req.permission,
|
||||
scopeData: {
|
||||
scope: AccessScope.Organization,
|
||||
orgId: req.permission.orgId
|
||||
},
|
||||
data: {
|
||||
identityId: req.params.identityId,
|
||||
roles: req.body.roles
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
identityMembership: { ...membership, identityId: req.params.identityId, orgId: req.permission.orgId }
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "DELETE",
|
||||
url: "/identity-memberships/:identityId",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
schema: {
|
||||
hide: true,
|
||||
tags: [ApiDocsTags.ProjectIdentities],
|
||||
description: "Delete org identity memberships",
|
||||
security: [
|
||||
{
|
||||
bearerAuth: []
|
||||
}
|
||||
],
|
||||
params: z.object({
|
||||
identityId: z.string().trim().describe(PROJECT_IDENTITIES.DELETE_IDENTITY_MEMBERSHIP.identityId)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
identityMembership: sanitizedOrgIdentityMembershipSchema
|
||||
})
|
||||
}
|
||||
},
|
||||
handler: async (req) => {
|
||||
const { membership } = await server.services.membershipIdentity.deleteMembership({
|
||||
permission: req.permission,
|
||||
scopeData: {
|
||||
scope: AccessScope.Organization,
|
||||
orgId: req.permission.orgId
|
||||
},
|
||||
selector: {
|
||||
identityId: req.params.identityId
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
identityMembership: { ...membership, identityId: req.params.identityId, orgId: req.permission.orgId }
|
||||
};
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -249,7 +249,7 @@ export const registerIdentityRouter = async (server: FastifyZodProvider) => {
|
||||
permissions: true,
|
||||
description: true
|
||||
}).optional(),
|
||||
identity: IdentitiesSchema.pick({ name: true, id: true, hasDeleteProtection: true }).extend({
|
||||
identity: IdentitiesSchema.pick({ name: true, id: true, hasDeleteProtection: true, orgId: true }).extend({
|
||||
authMethods: z.array(z.string()),
|
||||
activeLockoutAuthMethods: z.array(z.string())
|
||||
})
|
||||
@@ -393,7 +393,7 @@ export const registerIdentityRouter = async (server: FastifyZodProvider) => {
|
||||
permissions: true,
|
||||
description: true
|
||||
}).optional(),
|
||||
identity: IdentitiesSchema.pick({ name: true, id: true, hasDeleteProtection: true }).extend({
|
||||
identity: IdentitiesSchema.pick({ name: true, id: true, hasDeleteProtection: true, orgId: true }).extend({
|
||||
authMethods: z.array(z.string())
|
||||
})
|
||||
}).array(),
|
||||
|
||||
@@ -64,7 +64,7 @@ export const registerIdentityTlsCertAuthRouter = async (server: FastifyZodProvid
|
||||
throw new BadRequestError({ message: "Missing TLS certificate in header" });
|
||||
}
|
||||
|
||||
const { identityTlsCertAuth, accessToken, identityAccessToken, identityMembershipOrg } =
|
||||
const { identityTlsCertAuth, accessToken, identityAccessToken, identity } =
|
||||
await server.services.identityTlsCertAuth.login({
|
||||
identityId: req.body.identityId,
|
||||
clientCertificate: clientCertificate as string
|
||||
@@ -72,7 +72,7 @@ export const registerIdentityTlsCertAuthRouter = async (server: FastifyZodProvid
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
orgId: identity.orgId,
|
||||
event: {
|
||||
type: EventType.LOGIN_IDENTITY_TLS_CERT_AUTH,
|
||||
metadata: {
|
||||
|
||||
@@ -319,7 +319,7 @@ export const registerIdentityTokenAuthRouter = async (server: FastifyZodProvider
|
||||
}
|
||||
},
|
||||
handler: async (req) => {
|
||||
const { identityTokenAuth, accessToken, identityAccessToken, identityMembershipOrg } =
|
||||
const { identityTokenAuth, accessToken, identityAccessToken, identity } =
|
||||
await server.services.identityTokenAuth.createTokenAuthToken({
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
@@ -332,7 +332,7 @@ export const registerIdentityTokenAuthRouter = async (server: FastifyZodProvider
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
orgId: identity.orgId,
|
||||
event: {
|
||||
type: EventType.CREATE_TOKEN_IDENTITY_TOKEN_AUTH,
|
||||
metadata: {
|
||||
|
||||
@@ -52,14 +52,14 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
|
||||
accessToken,
|
||||
identityAccessToken,
|
||||
validClientSecretInfo,
|
||||
identityMembershipOrg,
|
||||
identity,
|
||||
accessTokenTTL,
|
||||
accessTokenMaxTTL
|
||||
} = await server.services.identityUa.login(req.body.clientId, req.body.clientSecret, req.realIp);
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
orgId: identity.orgId,
|
||||
event: {
|
||||
type: EventType.LOGIN_IDENTITY_UNIVERSAL_AUTH,
|
||||
metadata: {
|
||||
|
||||
@@ -33,6 +33,7 @@ import { registerIdentityKubernetesRouter } from "./identity-kubernetes-auth-rou
|
||||
import { registerIdentityLdapAuthRouter } from "./identity-ldap-auth-router";
|
||||
import { registerIdentityOciAuthRouter } from "./identity-oci-auth-router";
|
||||
import { registerIdentityOidcAuthRouter } from "./identity-oidc-auth-router";
|
||||
import { registerOrgIdentityMembershipRouter } from "./identity-org-membership-router";
|
||||
import { registerIdentityProjectRouter } from "./identity-project-router";
|
||||
import { registerIdentityRouter } from "./identity-router";
|
||||
import { registerIdentityTlsCertAuthRouter } from "./identity-tls-cert-auth-router";
|
||||
@@ -90,6 +91,7 @@ export const registerV1Routes = async (server: FastifyZodProvider) => {
|
||||
);
|
||||
await server.register(registerPasswordRouter, { prefix: "/password" });
|
||||
await server.register(registerOrgRouter, { prefix: "/organization" });
|
||||
await server.register(registerOrgIdentityMembershipRouter, { prefix: "/organization" });
|
||||
await server.register(registerAdminRouter, { prefix: "/admin" });
|
||||
await server.register(registerOrgAdminRouter, { prefix: "/organization-admin" });
|
||||
await server.register(registerUserRouter, { prefix: "/user" });
|
||||
|
||||
@@ -2,6 +2,7 @@ import RE2 from "re2";
|
||||
import { z } from "zod";
|
||||
|
||||
import {
|
||||
AccessScope,
|
||||
AuditLogsSchema,
|
||||
GroupsSchema,
|
||||
IncidentContactsSchema,
|
||||
@@ -59,7 +60,14 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
organization: sanitizedOrganizationSchema
|
||||
organization: sanitizedOrganizationSchema.extend({
|
||||
subOrganization: z
|
||||
.object({
|
||||
id: z.string(),
|
||||
name: z.string()
|
||||
})
|
||||
.optional()
|
||||
})
|
||||
})
|
||||
}
|
||||
},
|
||||
@@ -69,6 +77,7 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
|
||||
req.permission.id,
|
||||
req.params.organizationId,
|
||||
req.permission.authMethod,
|
||||
req.permission.rootOrgId,
|
||||
req.permission.orgId
|
||||
);
|
||||
return { organization };
|
||||
@@ -467,4 +476,68 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
|
||||
return { groups };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/users/available",
|
||||
schema: {
|
||||
response: {
|
||||
200: z.object({
|
||||
users: z
|
||||
.object({
|
||||
id: z.string().uuid(),
|
||||
username: z.string(),
|
||||
email: z.string().nullable().optional(),
|
||||
firstName: z.string().nullable().optional(),
|
||||
lastName: z.string().nullable().optional()
|
||||
})
|
||||
.array()
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
const { users } = await server.services.membershipUser.listAvailableUsers({
|
||||
permission: req.permission,
|
||||
scopeData: {
|
||||
orgId: req.permission.orgId,
|
||||
scope: AccessScope.Organization
|
||||
},
|
||||
data: {}
|
||||
});
|
||||
|
||||
return { users };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/identities/available",
|
||||
schema: {
|
||||
response: {
|
||||
200: z.object({
|
||||
identities: z
|
||||
.object({
|
||||
id: z.string().uuid(),
|
||||
name: z.string(),
|
||||
hasDeleteProtection: z.boolean()
|
||||
})
|
||||
.array()
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
const { identities } = await server.services.membershipIdentity.listAvailableIdentities({
|
||||
permission: req.permission,
|
||||
scopeData: {
|
||||
orgId: req.permission.orgId,
|
||||
scope: AccessScope.Organization
|
||||
},
|
||||
data: {}
|
||||
});
|
||||
|
||||
return { identities };
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -60,7 +60,7 @@ export const registerIdentityOrgRouter = async (server: FastifyZodProvider) => {
|
||||
permissions: true,
|
||||
description: true
|
||||
}).optional(),
|
||||
identity: IdentitiesSchema.pick({ name: true, id: true }).extend({
|
||||
identity: IdentitiesSchema.pick({ name: true, id: true, orgId: true }).extend({
|
||||
authMethods: z.array(z.string())
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ForbiddenError, subject } from "@casl/ability";
|
||||
|
||||
import { ActionProjectType, TAppConnections } from "@app/db/schemas";
|
||||
import { ActionProjectType, OrganizationActionScope, TAppConnections } from "@app/db/schemas";
|
||||
import { ValidateOCIConnectionCredentialsSchema } from "@app/ee/services/app-connections/oci";
|
||||
import { ociConnectionService } from "@app/ee/services/app-connections/oci/oci-connection-service";
|
||||
import { ValidateOracleDBConnectionCredentialsSchema } from "@app/ee/services/app-connections/oracledb";
|
||||
@@ -215,13 +215,14 @@ export const appConnectionServiceFactory = ({
|
||||
)
|
||||
);
|
||||
} else {
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
actor.type,
|
||||
actor.id,
|
||||
actor.orgId,
|
||||
actor.authMethod,
|
||||
actor.orgId
|
||||
);
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
actorId: actor.id,
|
||||
actor: actor.type,
|
||||
orgId: actor.orgId,
|
||||
actorOrgId: actor.orgId,
|
||||
actorAuthMethod: actor.authMethod,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionAppConnectionActions.Read,
|
||||
@@ -268,13 +269,14 @@ export const appConnectionServiceFactory = ({
|
||||
subject(ProjectPermissionSub.AppConnections, { connectionId })
|
||||
);
|
||||
} else {
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
actor.type,
|
||||
actor.id,
|
||||
appConnection.orgId,
|
||||
actor.authMethod,
|
||||
actor.orgId
|
||||
);
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
actorId: actor.id,
|
||||
actor: actor.type,
|
||||
orgId: appConnection.orgId,
|
||||
actorOrgId: actor.orgId,
|
||||
actorAuthMethod: actor.authMethod,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionAppConnectionActions.Read,
|
||||
@@ -318,13 +320,14 @@ export const appConnectionServiceFactory = ({
|
||||
subject(ProjectPermissionSub.AppConnections, { connectionId: appConnection.id })
|
||||
);
|
||||
} else {
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
actor.type,
|
||||
actor.id,
|
||||
appConnection.orgId,
|
||||
actor.authMethod,
|
||||
actor.orgId
|
||||
);
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
actorId: actor.id,
|
||||
actor: actor.type,
|
||||
orgId: appConnection.orgId,
|
||||
actorOrgId: actor.orgId,
|
||||
actorAuthMethod: actor.authMethod,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionAppConnectionActions.Read,
|
||||
@@ -342,13 +345,14 @@ export const appConnectionServiceFactory = ({
|
||||
{ method, app, credentials, gatewayId, projectId, ...params }: TCreateAppConnectionDTO,
|
||||
actor: OrgServiceActor
|
||||
) => {
|
||||
const { permission: orgPermission } = await permissionService.getOrgPermission(
|
||||
actor.type,
|
||||
actor.id,
|
||||
actor.orgId,
|
||||
actor.authMethod,
|
||||
actor.orgId
|
||||
);
|
||||
const { permission: orgPermission } = await permissionService.getOrgPermission({
|
||||
actorId: actor.id,
|
||||
actor: actor.type,
|
||||
orgId: actor.orgId,
|
||||
actorOrgId: actor.orgId,
|
||||
actorAuthMethod: actor.authMethod,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
|
||||
if (projectId) {
|
||||
const project = await projectDAL.findProjectById(projectId);
|
||||
@@ -477,13 +481,14 @@ export const appConnectionServiceFactory = ({
|
||||
"Failed to update app connection due to plan restriction. Upgrade plan to access enterprise app connections."
|
||||
);
|
||||
|
||||
const { permission: orgPermission } = await permissionService.getOrgPermission(
|
||||
actor.type,
|
||||
actor.id,
|
||||
appConnection.orgId,
|
||||
actor.authMethod,
|
||||
actor.orgId
|
||||
);
|
||||
const { permission: orgPermission } = await permissionService.getOrgPermission({
|
||||
actorId: actor.id,
|
||||
actor: actor.type,
|
||||
orgId: appConnection.orgId,
|
||||
actorOrgId: actor.orgId,
|
||||
actorAuthMethod: actor.authMethod,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
|
||||
if (appConnection.projectId) {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
@@ -635,13 +640,14 @@ export const appConnectionServiceFactory = ({
|
||||
subject(ProjectPermissionSub.AppConnections, { connectionId })
|
||||
);
|
||||
} else {
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
actor.type,
|
||||
actor.id,
|
||||
appConnection.orgId,
|
||||
actor.authMethod,
|
||||
actor.orgId
|
||||
);
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
actorId: actor.id,
|
||||
actor: actor.type,
|
||||
orgId: appConnection.orgId,
|
||||
actorOrgId: actor.orgId,
|
||||
actorAuthMethod: actor.authMethod,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionAppConnectionActions.Delete,
|
||||
@@ -704,13 +710,14 @@ export const appConnectionServiceFactory = ({
|
||||
subject(ProjectPermissionSub.AppConnections, { connectionId })
|
||||
);
|
||||
} else {
|
||||
const { permission: orgPermission } = await permissionService.getOrgPermission(
|
||||
actor.type,
|
||||
actor.id,
|
||||
appConnection.orgId,
|
||||
actor.authMethod,
|
||||
actor.orgId
|
||||
);
|
||||
const { permission: orgPermission } = await permissionService.getOrgPermission({
|
||||
actorId: actor.id,
|
||||
actor: actor.type,
|
||||
orgId: appConnection.orgId,
|
||||
actorOrgId: actor.orgId,
|
||||
actorAuthMethod: actor.authMethod,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
|
||||
ForbiddenError.from(orgPermission).throwUnlessCan(
|
||||
OrgPermissionAppConnectionActions.Connect,
|
||||
@@ -747,13 +754,14 @@ export const appConnectionServiceFactory = ({
|
||||
};
|
||||
|
||||
const listAvailableAppConnectionsForUser = async (app: AppConnection, actor: OrgServiceActor, projectId?: string) => {
|
||||
const { permission: orgPermission } = await permissionService.getOrgPermission(
|
||||
actor.type,
|
||||
actor.id,
|
||||
actor.orgId,
|
||||
actor.authMethod,
|
||||
actor.orgId
|
||||
);
|
||||
const { permission: orgPermission } = await permissionService.getOrgPermission({
|
||||
actorId: actor.id,
|
||||
actor: actor.type,
|
||||
orgId: actor.orgId,
|
||||
actorOrgId: actor.orgId,
|
||||
actorAuthMethod: actor.authMethod,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
|
||||
let availableProjectConnections: TAppConnections[] = [];
|
||||
|
||||
@@ -805,13 +813,14 @@ export const appConnectionServiceFactory = ({
|
||||
|
||||
if (!appConnection) throw new NotFoundError({ message: `Could not find App Connection with ID ${connectionId}` });
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
actor.type,
|
||||
actor.id,
|
||||
appConnection.orgId,
|
||||
actor.authMethod,
|
||||
actor.orgId
|
||||
);
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
actorId: actor.id,
|
||||
actor: actor.type,
|
||||
orgId: appConnection.orgId,
|
||||
actorOrgId: actor.orgId,
|
||||
actorAuthMethod: actor.authMethod,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionAppConnectionActions.Read,
|
||||
|
||||
@@ -3,10 +3,11 @@ import { Knex } from "knex";
|
||||
import { AccessScope, TAuthTokens, TAuthTokenSessions } from "@app/db/schemas";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { crypto } from "@app/lib/crypto/cryptography";
|
||||
import { ForbiddenRequestError, NotFoundError, UnauthorizedError } from "@app/lib/errors";
|
||||
import { BadRequestError, ForbiddenRequestError, NotFoundError, UnauthorizedError } from "@app/lib/errors";
|
||||
|
||||
import { AuthModeJwtTokenPayload, AuthModeRefreshJwtTokenPayload, AuthTokenType } from "../auth/auth-type";
|
||||
import { TMembershipUserDALFactory } from "../membership-user/membership-user-dal";
|
||||
import { TOrgDALFactory } from "../org/org-dal";
|
||||
import { TUserDALFactory } from "../user/user-dal";
|
||||
import { TTokenDALFactory } from "./auth-token-dal";
|
||||
import { TCreateTokenForUserDTO, TIssueAuthTokenDTO, TokenType, TValidateTokenForUserDTO } from "./auth-token-types";
|
||||
@@ -14,6 +15,7 @@ import { TCreateTokenForUserDTO, TIssueAuthTokenDTO, TokenType, TValidateTokenFo
|
||||
type TAuthTokenServiceFactoryDep = {
|
||||
tokenDAL: TTokenDALFactory;
|
||||
userDAL: Pick<TUserDALFactory, "findById" | "transaction">;
|
||||
orgDAL: Pick<TOrgDALFactory, "findOne">;
|
||||
membershipUserDAL: Pick<TMembershipUserDALFactory, "findOne">;
|
||||
};
|
||||
|
||||
@@ -80,7 +82,7 @@ export const getTokenConfig = (tokenType: TokenType) => {
|
||||
}
|
||||
};
|
||||
|
||||
export const tokenServiceFactory = ({ tokenDAL, userDAL, membershipUserDAL }: TAuthTokenServiceFactoryDep) => {
|
||||
export const tokenServiceFactory = ({ tokenDAL, userDAL, membershipUserDAL, orgDAL }: TAuthTokenServiceFactoryDep) => {
|
||||
const createTokenForUser = async ({ type, userId, orgId, aliasId, payload }: TCreateTokenForUserDTO) => {
|
||||
const { token, ...tkCfg } = getTokenConfig(type);
|
||||
const appCfg = getConfig();
|
||||
@@ -194,7 +196,7 @@ export const tokenServiceFactory = ({ tokenDAL, userDAL, membershipUserDAL }: TA
|
||||
};
|
||||
|
||||
// to parse jwt identity in inject identity plugin
|
||||
const fnValidateJwtIdentity = async (token: AuthModeJwtTokenPayload) => {
|
||||
const fnValidateJwtIdentity = async (token: AuthModeJwtTokenPayload, subOrganizationSelector?: string) => {
|
||||
const session = await tokenDAL.findOneTokenSession({
|
||||
id: token.tokenVersionId,
|
||||
userId: token.userId
|
||||
@@ -207,7 +209,35 @@ export const tokenServiceFactory = ({ tokenDAL, userDAL, membershipUserDAL }: TA
|
||||
const user = await userDAL.findById(session.userId);
|
||||
if (!user || !user.isAccepted) throw new NotFoundError({ message: `User with ID '${session.userId}' not found` });
|
||||
|
||||
let orgId = "";
|
||||
let rootOrgId = "";
|
||||
let parentOrgId = "";
|
||||
if (token.organizationId) {
|
||||
if (subOrganizationSelector) {
|
||||
const subOrganization = await orgDAL.findOne({
|
||||
rootOrgId: token.organizationId,
|
||||
slug: subOrganizationSelector
|
||||
});
|
||||
if (!subOrganization)
|
||||
throw new BadRequestError({ message: `Sub organization ${subOrganizationSelector} not found` });
|
||||
|
||||
const orgMembership = await membershipUserDAL.findOne({
|
||||
actorUserId: user.id,
|
||||
scopeOrgId: subOrganization.id,
|
||||
scope: AccessScope.Organization
|
||||
});
|
||||
|
||||
if (!orgMembership) {
|
||||
throw new ForbiddenRequestError({ message: "User not member of organization" });
|
||||
}
|
||||
|
||||
if (!orgMembership.isActive) {
|
||||
throw new ForbiddenRequestError({ message: "User organization membership is inactive" });
|
||||
}
|
||||
orgId = subOrganization.id;
|
||||
rootOrgId = token.organizationId;
|
||||
parentOrgId = subOrganization.parentOrgId as string;
|
||||
} else {
|
||||
const orgMembership = await membershipUserDAL.findOne({
|
||||
actorUserId: user.id,
|
||||
scopeOrgId: token.organizationId,
|
||||
@@ -217,12 +247,18 @@ export const tokenServiceFactory = ({ tokenDAL, userDAL, membershipUserDAL }: TA
|
||||
if (!orgMembership) {
|
||||
throw new ForbiddenRequestError({ message: "User not member of organization" });
|
||||
}
|
||||
|
||||
if (!orgMembership.isActive) {
|
||||
throw new ForbiddenRequestError({ message: "User organization membership is inactive" });
|
||||
}
|
||||
|
||||
orgId = token.organizationId;
|
||||
rootOrgId = token.organizationId;
|
||||
parentOrgId = token.organizationId;
|
||||
}
|
||||
}
|
||||
|
||||
return { user, tokenVersionId: token.tokenVersionId, orgId: token.organizationId };
|
||||
return { user, tokenVersionId: token.tokenVersionId, orgId, rootOrgId, parentOrgId };
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
@@ -258,7 +258,13 @@ export const authSignupServiceFactory = ({
|
||||
let refreshTokenExpiresIn: string | number = appCfg.JWT_REFRESH_LIFETIME;
|
||||
|
||||
if (organizationId) {
|
||||
const org = await orgService.findOrganizationById(user.id, organizationId, authMethod, organizationId);
|
||||
const org = await orgService.findOrganizationById(
|
||||
user.id,
|
||||
organizationId,
|
||||
authMethod,
|
||||
organizationId,
|
||||
organizationId
|
||||
);
|
||||
if (org && org.userTokenExpiration) {
|
||||
tokenSessionExpiresIn = getMinExpiresIn(appCfg.JWT_AUTH_LIFETIME, org.userTokenExpiration);
|
||||
refreshTokenExpiresIn = org.userTokenExpiration;
|
||||
|
||||
+17
-14
@@ -1,5 +1,6 @@
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
|
||||
import { OrganizationActionScope } 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";
|
||||
@@ -26,13 +27,14 @@ export const externalGroupOrgRoleMappingServiceFactory = ({
|
||||
roleDAL
|
||||
}: TExternalGroupOrgRoleMappingServiceFactoryDep) => {
|
||||
const listExternalGroupOrgRoleMappings = async (actor: OrgServiceActor) => {
|
||||
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.ParentOrganization
|
||||
});
|
||||
|
||||
// TODO: will need to change if we add support for ldap, oidc, etc.
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Scim);
|
||||
@@ -48,13 +50,14 @@ export const externalGroupOrgRoleMappingServiceFactory = ({
|
||||
dto: TSyncExternalGroupOrgMembershipRoleMappingsDTO,
|
||||
actor: OrgServiceActor
|
||||
) => {
|
||||
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.ParentOrganization
|
||||
});
|
||||
|
||||
// TODO: will need to change if we add support for ldap, oidc, etc.
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Scim);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { OrgMembershipRole } from "@app/db/schemas";
|
||||
import { OrganizationActionScope, OrgMembershipRole } from "@app/db/schemas";
|
||||
import {
|
||||
AuditLogInfo,
|
||||
EventType,
|
||||
@@ -89,13 +89,14 @@ export const externalMigrationServiceFactory = ({
|
||||
throw new BadRequestError({ message: "EnvKey migration is not supported when running in FIPS mode." });
|
||||
}
|
||||
|
||||
const { hasRole } = await permissionService.getOrgPermission(
|
||||
actor,
|
||||
const { hasRole } = await permissionService.getOrgPermission({
|
||||
actorId,
|
||||
actor,
|
||||
orgId: actorOrgId,
|
||||
actorOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
if (!hasRole(OrgMembershipRole.Admin)) {
|
||||
throw new ForbiddenRequestError({ message: "Only admins can import data" });
|
||||
}
|
||||
@@ -136,13 +137,14 @@ export const externalMigrationServiceFactory = ({
|
||||
actorOrgId,
|
||||
actorAuthMethod
|
||||
}: TImportVaultDataDTO) => {
|
||||
const { hasRole } = await permissionService.getOrgPermission(
|
||||
actor,
|
||||
const { hasRole } = await permissionService.getOrgPermission({
|
||||
actorId,
|
||||
actor,
|
||||
orgId: actorOrgId,
|
||||
actorOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
|
||||
if (!hasRole(OrgMembershipRole.Admin)) {
|
||||
throw new ForbiddenRequestError({ message: "Only admins can import data" });
|
||||
@@ -192,13 +194,14 @@ export const externalMigrationServiceFactory = ({
|
||||
actorAuthMethod,
|
||||
provider
|
||||
}: THasCustomVaultMigrationDTO) => {
|
||||
const { hasRole } = await permissionService.getOrgPermission(
|
||||
actor,
|
||||
const { hasRole } = await permissionService.getOrgPermission({
|
||||
actorId,
|
||||
actor,
|
||||
orgId: actorOrgId,
|
||||
actorOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
|
||||
if (!hasRole(OrgMembershipRole.Admin)) {
|
||||
throw new ForbiddenRequestError({ message: "Only admins can check custom migration status" });
|
||||
@@ -247,13 +250,14 @@ export const externalMigrationServiceFactory = ({
|
||||
};
|
||||
|
||||
const createVaultExternalMigration = async ({ namespace, connectionId, actor }: TCreateVaultExternalMigrationDTO) => {
|
||||
const { hasRole } = await permissionService.getOrgPermission(
|
||||
actor.type,
|
||||
actor.id,
|
||||
actor.orgId,
|
||||
actor.authMethod,
|
||||
actor.orgId
|
||||
);
|
||||
const { hasRole } = await permissionService.getOrgPermission({
|
||||
actorId: actor.id,
|
||||
actor: actor.type,
|
||||
orgId: actor.orgId,
|
||||
actorOrgId: actor.orgId,
|
||||
actorAuthMethod: actor.authMethod,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
|
||||
if (!hasRole(OrgMembershipRole.Admin)) {
|
||||
throw new ForbiddenRequestError({ message: "Only admins can configure vault external migration" });
|
||||
@@ -298,13 +302,14 @@ export const externalMigrationServiceFactory = ({
|
||||
connectionId,
|
||||
actor
|
||||
}: TUpdateVaultExternalMigrationDTO) => {
|
||||
const { hasRole } = await permissionService.getOrgPermission(
|
||||
actor.type,
|
||||
actor.id,
|
||||
actor.orgId,
|
||||
actor.authMethod,
|
||||
actor.orgId
|
||||
);
|
||||
const { hasRole } = await permissionService.getOrgPermission({
|
||||
actorId: actor.id,
|
||||
actor: actor.type,
|
||||
orgId: actor.orgId,
|
||||
actorOrgId: actor.orgId,
|
||||
actorAuthMethod: actor.authMethod,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
|
||||
if (!hasRole(OrgMembershipRole.Admin)) {
|
||||
throw new ForbiddenRequestError({ message: "Only admins can update vault external migration" });
|
||||
@@ -332,13 +337,14 @@ export const externalMigrationServiceFactory = ({
|
||||
};
|
||||
|
||||
const getVaultExternalMigrationConfigs = async ({ actor }: { actor: OrgServiceActor }) => {
|
||||
const { hasRole } = await permissionService.getOrgPermission(
|
||||
actor.type,
|
||||
actor.id,
|
||||
actor.orgId,
|
||||
actor.authMethod,
|
||||
actor.orgId
|
||||
);
|
||||
const { hasRole } = await permissionService.getOrgPermission({
|
||||
actorId: actor.id,
|
||||
actor: actor.type,
|
||||
orgId: actor.orgId,
|
||||
actorOrgId: actor.orgId,
|
||||
actorAuthMethod: actor.authMethod,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
|
||||
if (!hasRole(OrgMembershipRole.Admin)) {
|
||||
throw new ForbiddenRequestError({ message: "Only admins can view vault external migration configs" });
|
||||
@@ -352,13 +358,14 @@ export const externalMigrationServiceFactory = ({
|
||||
};
|
||||
|
||||
const getVaultNamespaces = async ({ actor }: { actor: OrgServiceActor }) => {
|
||||
const { hasRole } = await permissionService.getOrgPermission(
|
||||
actor.type,
|
||||
actor.id,
|
||||
actor.orgId,
|
||||
actor.authMethod,
|
||||
actor.orgId
|
||||
);
|
||||
const { hasRole } = await permissionService.getOrgPermission({
|
||||
actorId: actor.id,
|
||||
actor: actor.type,
|
||||
orgId: actor.orgId,
|
||||
actorOrgId: actor.orgId,
|
||||
actorAuthMethod: actor.authMethod,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
|
||||
if (!hasRole(OrgMembershipRole.Admin)) {
|
||||
throw new ForbiddenRequestError({ message: "Only admins can view vault namespaces" });
|
||||
@@ -380,13 +387,14 @@ export const externalMigrationServiceFactory = ({
|
||||
};
|
||||
|
||||
const getVaultPolicies = async ({ actor, namespace }: { actor: OrgServiceActor; namespace: string }) => {
|
||||
const { hasRole } = await permissionService.getOrgPermission(
|
||||
actor.type,
|
||||
actor.id,
|
||||
actor.orgId,
|
||||
actor.authMethod,
|
||||
actor.orgId
|
||||
);
|
||||
const { hasRole } = await permissionService.getOrgPermission({
|
||||
actorId: actor.id,
|
||||
actor: actor.type,
|
||||
orgId: actor.orgId,
|
||||
actorOrgId: actor.orgId,
|
||||
actorAuthMethod: actor.authMethod,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
|
||||
if (!hasRole(OrgMembershipRole.Admin)) {
|
||||
throw new ForbiddenRequestError({ message: "Only admins can view vault policies" });
|
||||
@@ -422,13 +430,14 @@ export const externalMigrationServiceFactory = ({
|
||||
};
|
||||
|
||||
const getVaultMounts = async ({ actor, namespace }: { actor: OrgServiceActor; namespace: string }) => {
|
||||
const { hasRole } = await permissionService.getOrgPermission(
|
||||
actor.type,
|
||||
actor.id,
|
||||
actor.orgId,
|
||||
actor.authMethod,
|
||||
actor.orgId
|
||||
);
|
||||
const { hasRole } = await permissionService.getOrgPermission({
|
||||
actorId: actor.id,
|
||||
actor: actor.type,
|
||||
orgId: actor.orgId,
|
||||
actorOrgId: actor.orgId,
|
||||
actorAuthMethod: actor.authMethod,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
|
||||
if (!hasRole(OrgMembershipRole.Admin)) {
|
||||
throw new ForbiddenRequestError({ message: "Only admins can view vault mounts" });
|
||||
@@ -472,13 +481,14 @@ export const externalMigrationServiceFactory = ({
|
||||
namespace: string;
|
||||
mountPath: string;
|
||||
}) => {
|
||||
const { hasRole } = await permissionService.getOrgPermission(
|
||||
actor.type,
|
||||
actor.id,
|
||||
actor.orgId,
|
||||
actor.authMethod,
|
||||
actor.orgId
|
||||
);
|
||||
const { hasRole } = await permissionService.getOrgPermission({
|
||||
actorId: actor.id,
|
||||
actor: actor.type,
|
||||
orgId: actor.orgId,
|
||||
actorOrgId: actor.orgId,
|
||||
actorAuthMethod: actor.authMethod,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
|
||||
if (!hasRole(OrgMembershipRole.Admin)) {
|
||||
throw new ForbiddenRequestError({ message: "Only admins can view vault secret paths" });
|
||||
@@ -531,13 +541,14 @@ export const externalMigrationServiceFactory = ({
|
||||
vaultSecretPath: string;
|
||||
auditLogInfo: AuditLogInfo;
|
||||
}) => {
|
||||
const { hasRole } = await permissionService.getOrgPermission(
|
||||
actor.type,
|
||||
actor.id,
|
||||
actor.orgId,
|
||||
actor.authMethod,
|
||||
actor.orgId
|
||||
);
|
||||
const { hasRole } = await permissionService.getOrgPermission({
|
||||
actorId: actor.id,
|
||||
actor: actor.type,
|
||||
orgId: actor.orgId,
|
||||
actorOrgId: actor.orgId,
|
||||
actorAuthMethod: actor.authMethod,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
|
||||
if (!hasRole(OrgMembershipRole.Admin)) {
|
||||
throw new ForbiddenRequestError({ message: "Only admins can import vault secrets" });
|
||||
@@ -617,13 +628,14 @@ export const externalMigrationServiceFactory = ({
|
||||
};
|
||||
|
||||
const deleteVaultExternalMigration = async ({ id, actor }: TDeleteVaultExternalMigrationDTO) => {
|
||||
const { hasRole } = await permissionService.getOrgPermission(
|
||||
actor.type,
|
||||
actor.id,
|
||||
actor.orgId,
|
||||
actor.authMethod,
|
||||
actor.orgId
|
||||
);
|
||||
const { hasRole } = await permissionService.getOrgPermission({
|
||||
actorId: actor.id,
|
||||
actor: actor.type,
|
||||
orgId: actor.orgId,
|
||||
actorOrgId: actor.orgId,
|
||||
actorAuthMethod: actor.authMethod,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
|
||||
if (!hasRole(OrgMembershipRole.Admin)) {
|
||||
throw new ForbiddenRequestError({ message: "Only admins can delete vault external migration configs" });
|
||||
@@ -653,13 +665,14 @@ export const externalMigrationServiceFactory = ({
|
||||
namespace: string;
|
||||
authType?: string;
|
||||
}) => {
|
||||
const { hasRole } = await permissionService.getOrgPermission(
|
||||
actor.type,
|
||||
actor.id,
|
||||
actor.orgId,
|
||||
actor.authMethod,
|
||||
actor.orgId
|
||||
);
|
||||
const { hasRole } = await permissionService.getOrgPermission({
|
||||
actorId: actor.id,
|
||||
actor: actor.type,
|
||||
orgId: actor.orgId,
|
||||
actorOrgId: actor.orgId,
|
||||
actorAuthMethod: actor.authMethod,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
|
||||
if (!hasRole(OrgMembershipRole.Admin)) {
|
||||
throw new ForbiddenRequestError({ message: "Only admins can view vault auth mounts" });
|
||||
@@ -704,13 +717,14 @@ export const externalMigrationServiceFactory = ({
|
||||
namespace: string;
|
||||
mountPath: string;
|
||||
}) => {
|
||||
const { hasRole } = await permissionService.getOrgPermission(
|
||||
actor.type,
|
||||
actor.id,
|
||||
actor.orgId,
|
||||
actor.authMethod,
|
||||
actor.orgId
|
||||
);
|
||||
const { hasRole } = await permissionService.getOrgPermission({
|
||||
actorId: actor.id,
|
||||
actor: actor.type,
|
||||
orgId: actor.orgId,
|
||||
actorOrgId: actor.orgId,
|
||||
actorAuthMethod: actor.authMethod,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
|
||||
if (!hasRole(OrgMembershipRole.Admin)) {
|
||||
throw new ForbiddenRequestError({ message: "Only admins can view vault Kubernetes auth roles" });
|
||||
|
||||
@@ -19,6 +19,7 @@ export const identityAccessTokenDALFactory = (db: TDbClient) => {
|
||||
.join(TableName.Identity, `${TableName.Identity}.id`, `${TableName.IdentityAccessToken}.identityId`)
|
||||
.select(selectAllTableCols(TableName.IdentityAccessToken))
|
||||
.select(db.ref("name").withSchema(TableName.Identity))
|
||||
.select(db.ref("orgId").withSchema(TableName.Identity).as("identityScopeOrgId"))
|
||||
.first();
|
||||
|
||||
return doc;
|
||||
|
||||
@@ -8,6 +8,7 @@ import { TAccessTokenQueueServiceFactory } from "../access-token-queue/access-to
|
||||
import { AuthTokenType } from "../auth/auth-type";
|
||||
import { TIdentityDALFactory } from "../identity/identity-dal";
|
||||
import { TMembershipIdentityDALFactory } from "../membership-identity/membership-identity-dal";
|
||||
import { TOrgDALFactory } from "../org/org-dal";
|
||||
import { TIdentityAccessTokenDALFactory } from "./identity-access-token-dal";
|
||||
import { TIdentityAccessTokenJwtPayload, TRenewAccessTokenDTO } from "./identity-access-token-types";
|
||||
|
||||
@@ -19,6 +20,7 @@ type TIdentityAccessTokenServiceFactoryDep = {
|
||||
"updateIdentityAccessTokenStatus" | "getIdentityTokenDetailsInCache"
|
||||
>;
|
||||
membershipIdentityDAL: Pick<TMembershipIdentityDALFactory, "findOne">;
|
||||
orgDAL: Pick<TOrgDALFactory, "findOne">;
|
||||
};
|
||||
|
||||
export type TIdentityAccessTokenServiceFactory = ReturnType<typeof identityAccessTokenServiceFactory>;
|
||||
@@ -27,7 +29,8 @@ export const identityAccessTokenServiceFactory = ({
|
||||
identityAccessTokenDAL,
|
||||
accessTokenQueue,
|
||||
identityDAL,
|
||||
membershipIdentityDAL
|
||||
membershipIdentityDAL,
|
||||
orgDAL
|
||||
}: TIdentityAccessTokenServiceFactoryDep) => {
|
||||
const validateAccessTokenExp = async (identityAccessToken: TIdentityAccessTokens) => {
|
||||
const {
|
||||
@@ -181,7 +184,11 @@ export const identityAccessTokenServiceFactory = ({
|
||||
return { revokedToken };
|
||||
};
|
||||
|
||||
const fnValidateIdentityAccessToken = async (token: TIdentityAccessTokenJwtPayload, ipAddress?: string) => {
|
||||
const fnValidateIdentityAccessToken = async (
|
||||
token: TIdentityAccessTokenJwtPayload,
|
||||
subOrganizationSelector?: string,
|
||||
ipAddress?: string
|
||||
) => {
|
||||
const identityAccessToken = await identityAccessTokenDAL.findOne({
|
||||
[`${TableName.IdentityAccessToken}.id` as "id"]: token.identityAccessTokenId,
|
||||
isAccessTokenRevoked: false
|
||||
@@ -202,14 +209,41 @@ export const identityAccessTokenServiceFactory = ({
|
||||
trustedIps: trustedIps as TIp[]
|
||||
});
|
||||
}
|
||||
let orgId = "";
|
||||
let parentOrgId = "";
|
||||
const identityOrgDetails = await orgDAL.findOne({ id: identityAccessToken.identityScopeOrgId });
|
||||
const rootOrgId = identityOrgDetails.rootOrgId || identityOrgDetails.id;
|
||||
|
||||
if (subOrganizationSelector) {
|
||||
const subOrganization = await orgDAL.findOne({ rootOrgId, slug: subOrganizationSelector });
|
||||
if (!subOrganization)
|
||||
throw new BadRequestError({ message: `Sub organization ${subOrganizationSelector} not found` });
|
||||
|
||||
const identityOrgMembership = await membershipIdentityDAL.findOne({
|
||||
scope: AccessScope.Organization,
|
||||
actorIdentityId: identityAccessToken.identityId
|
||||
actorIdentityId: identityAccessToken.identityId,
|
||||
scopeOrgId: subOrganization.id
|
||||
});
|
||||
|
||||
if (!identityOrgMembership) {
|
||||
throw new BadRequestError({ message: "Identity does not belong to any organization" });
|
||||
}
|
||||
orgId = subOrganization.id;
|
||||
parentOrgId = subOrganization.parentOrgId as string;
|
||||
} else {
|
||||
const identityOrgMembership = await membershipIdentityDAL.findOne({
|
||||
scope: AccessScope.Organization,
|
||||
actorIdentityId: identityAccessToken.identityId,
|
||||
scopeOrgId: rootOrgId
|
||||
});
|
||||
|
||||
if (!identityOrgMembership) {
|
||||
throw new BadRequestError({ message: "Identity does not belong to any organization" });
|
||||
}
|
||||
|
||||
orgId = rootOrgId;
|
||||
parentOrgId = rootOrgId;
|
||||
}
|
||||
|
||||
let { accessTokenNumUses } = identityAccessToken;
|
||||
const tokenStatusInCache = await accessTokenQueue.getIdentityTokenDetailsInCache(identityAccessToken.id);
|
||||
@@ -219,7 +253,7 @@ export const identityAccessTokenServiceFactory = ({
|
||||
await validateAccessTokenExp({ ...identityAccessToken, accessTokenNumUses });
|
||||
|
||||
await accessTokenQueue.updateIdentityAccessTokenStatus(identityAccessToken.id, Number(accessTokenNumUses) + 1);
|
||||
return { ...identityAccessToken, orgId: identityOrgMembership.scopeOrgId };
|
||||
return { ...identityAccessToken, orgId, rootOrgId, parentOrgId };
|
||||
};
|
||||
|
||||
return { renewAccessToken, revokeAccessToken, fnValidateIdentityAccessToken };
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
import { AxiosError } from "axios";
|
||||
|
||||
import { AccessScope, IdentityAuthMethod } from "@app/db/schemas";
|
||||
import { AccessScope, IdentityAuthMethod, OrganizationActionScope } from "@app/db/schemas";
|
||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||
import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
|
||||
import {
|
||||
@@ -13,11 +13,18 @@ import { TPermissionServiceFactory } from "@app/ee/services/permission/permissio
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { request } from "@app/lib/config/request";
|
||||
import { crypto } from "@app/lib/crypto";
|
||||
import { BadRequestError, NotFoundError, PermissionBoundaryError, UnauthorizedError } from "@app/lib/errors";
|
||||
import {
|
||||
BadRequestError,
|
||||
ForbiddenRequestError,
|
||||
NotFoundError,
|
||||
PermissionBoundaryError,
|
||||
UnauthorizedError
|
||||
} from "@app/lib/errors";
|
||||
import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
|
||||
import { logger } from "@app/lib/logger";
|
||||
|
||||
import { ActorType, AuthTokenType } from "../auth/auth-type";
|
||||
import { TIdentityDALFactory } from "../identity/identity-dal";
|
||||
import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal";
|
||||
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
|
||||
import { TMembershipIdentityDALFactory } from "../membership-identity/membership-identity-dal";
|
||||
@@ -34,12 +41,13 @@ import {
|
||||
} from "./identity-alicloud-auth-types";
|
||||
|
||||
type TIdentityAliCloudAuthServiceFactoryDep = {
|
||||
identityDAL: Pick<TIdentityDALFactory, "findById">;
|
||||
identityAccessTokenDAL: Pick<TIdentityAccessTokenDALFactory, "create" | "delete">;
|
||||
identityAliCloudAuthDAL: Pick<
|
||||
TIdentityAliCloudAuthDALFactory,
|
||||
"findOne" | "transaction" | "create" | "updateById" | "delete"
|
||||
>;
|
||||
membershipIdentityDAL: Pick<TMembershipIdentityDALFactory, "findOne" | "updateById" | "getIdentityById">;
|
||||
membershipIdentityDAL: Pick<TMembershipIdentityDALFactory, "findOne" | "update" | "getIdentityById">;
|
||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
||||
orgDAL: Pick<TOrgDALFactory, "findById">;
|
||||
@@ -48,6 +56,7 @@ type TIdentityAliCloudAuthServiceFactoryDep = {
|
||||
export type TIdentityAliCloudAuthServiceFactory = ReturnType<typeof identityAliCloudAuthServiceFactory>;
|
||||
|
||||
export const identityAliCloudAuthServiceFactory = ({
|
||||
identityDAL,
|
||||
identityAccessTokenDAL,
|
||||
identityAliCloudAuthDAL,
|
||||
membershipIdentityDAL,
|
||||
@@ -63,12 +72,8 @@ export const identityAliCloudAuthServiceFactory = ({
|
||||
});
|
||||
}
|
||||
|
||||
const identityMembershipOrg = await membershipIdentityDAL.findOne({
|
||||
actorIdentityId: identityAliCloudAuth.identityId,
|
||||
scope: AccessScope.Organization
|
||||
});
|
||||
|
||||
if (!identityMembershipOrg) throw new UnauthorizedError({ message: "Identity not attached to a organization" });
|
||||
const identity = await identityDAL.findById(identityAliCloudAuth.identityId);
|
||||
if (!identity) throw new UnauthorizedError({ message: "Identity not found" });
|
||||
|
||||
const requestUrl = new URL("https://sts.aliyuncs.com");
|
||||
|
||||
@@ -93,8 +98,8 @@ export const identityAliCloudAuthServiceFactory = ({
|
||||
|
||||
// Generate the token
|
||||
const identityAccessToken = await identityAliCloudAuthDAL.transaction(async (tx) => {
|
||||
await membershipIdentityDAL.updateById(
|
||||
identityMembershipOrg.id,
|
||||
await membershipIdentityDAL.update(
|
||||
{ scope: AccessScope.Organization, scopeOrgId: identity.orgId, actorIdentityId: identity.id },
|
||||
{
|
||||
lastLoginAuthMethod: IdentityAuthMethod.ALICLOUD_AUTH,
|
||||
lastLoginTime: new Date()
|
||||
@@ -135,7 +140,7 @@ export const identityAliCloudAuthServiceFactory = ({
|
||||
identityAliCloudAuth,
|
||||
accessToken,
|
||||
identityAccessToken,
|
||||
identityMembershipOrg
|
||||
identity
|
||||
};
|
||||
};
|
||||
|
||||
@@ -162,6 +167,9 @@ export const identityAliCloudAuthServiceFactory = ({
|
||||
identityId
|
||||
});
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
if (identityMembershipOrg.identity.identityOrgId !== actorOrgId) {
|
||||
throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" });
|
||||
}
|
||||
|
||||
if (identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.ALICLOUD_AUTH)) {
|
||||
throw new BadRequestError({
|
||||
@@ -173,13 +181,14 @@ export const identityAliCloudAuthServiceFactory = ({
|
||||
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Create, OrgPermissionSubjects.Identity);
|
||||
|
||||
const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId);
|
||||
@@ -238,6 +247,9 @@ export const identityAliCloudAuthServiceFactory = ({
|
||||
identityId
|
||||
});
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
if (identityMembershipOrg.identity.identityOrgId !== actorOrgId) {
|
||||
throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" });
|
||||
}
|
||||
|
||||
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.ALICLOUD_AUTH)) {
|
||||
throw new NotFoundError({
|
||||
@@ -255,13 +267,14 @@ export const identityAliCloudAuthServiceFactory = ({
|
||||
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
|
||||
const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId);
|
||||
@@ -304,6 +317,9 @@ export const identityAliCloudAuthServiceFactory = ({
|
||||
identityId
|
||||
});
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
if (identityMembershipOrg.identity.identityOrgId !== actorOrgId) {
|
||||
throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" });
|
||||
}
|
||||
|
||||
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.ALICLOUD_AUTH)) {
|
||||
throw new BadRequestError({
|
||||
@@ -313,13 +329,14 @@ export const identityAliCloudAuthServiceFactory = ({
|
||||
|
||||
const alicloudIdentityAuth = await identityAliCloudAuthDAL.findOne({ identityId });
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity);
|
||||
return { ...alicloudIdentityAuth, orgId: identityMembershipOrg.scopeOrgId };
|
||||
};
|
||||
@@ -339,27 +356,32 @@ export const identityAliCloudAuthServiceFactory = ({
|
||||
identityId
|
||||
});
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
if (identityMembershipOrg.identity.identityOrgId !== actorOrgId) {
|
||||
throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" });
|
||||
}
|
||||
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.ALICLOUD_AUTH)) {
|
||||
throw new BadRequestError({
|
||||
message: "The identity does not have Alibaba Cloud auth"
|
||||
});
|
||||
}
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission(
|
||||
ActorType.IDENTITY,
|
||||
identityMembershipOrg.identity.id,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor: ActorType.IDENTITY,
|
||||
actorId: identityMembershipOrg.identity.id,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
|
||||
const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId);
|
||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||
|
||||
@@ -3,7 +3,7 @@ import { ForbiddenError } from "@casl/ability";
|
||||
import axios from "axios";
|
||||
import RE2 from "re2";
|
||||
|
||||
import { AccessScope, IdentityAuthMethod } from "@app/db/schemas";
|
||||
import { AccessScope, IdentityAuthMethod, OrganizationActionScope } from "@app/db/schemas";
|
||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||
import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
|
||||
import {
|
||||
@@ -13,10 +13,17 @@ import {
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { crypto } from "@app/lib/crypto";
|
||||
import { BadRequestError, NotFoundError, PermissionBoundaryError, UnauthorizedError } from "@app/lib/errors";
|
||||
import {
|
||||
BadRequestError,
|
||||
ForbiddenRequestError,
|
||||
NotFoundError,
|
||||
PermissionBoundaryError,
|
||||
UnauthorizedError
|
||||
} from "@app/lib/errors";
|
||||
import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
|
||||
|
||||
import { ActorType, AuthTokenType } from "../auth/auth-type";
|
||||
import { TIdentityDALFactory } from "../identity/identity-dal";
|
||||
import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal";
|
||||
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
|
||||
import { TMembershipIdentityDALFactory } from "../membership-identity/membership-identity-dal";
|
||||
@@ -35,9 +42,10 @@ import {
|
||||
} from "./identity-aws-auth-types";
|
||||
|
||||
type TIdentityAwsAuthServiceFactoryDep = {
|
||||
identityDAL: Pick<TIdentityDALFactory, "findById">;
|
||||
identityAccessTokenDAL: Pick<TIdentityAccessTokenDALFactory, "create" | "delete">;
|
||||
identityAwsAuthDAL: Pick<TIdentityAwsAuthDALFactory, "findOne" | "transaction" | "create" | "updateById" | "delete">;
|
||||
membershipIdentityDAL: Pick<TMembershipIdentityDALFactory, "findOne" | "updateById" | "getIdentityById">;
|
||||
membershipIdentityDAL: Pick<TMembershipIdentityDALFactory, "findOne" | "update" | "getIdentityById">;
|
||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
||||
orgDAL: Pick<TOrgDALFactory, "findById">;
|
||||
@@ -80,6 +88,7 @@ function isValidAwsRegion(region: string | null): boolean {
|
||||
}
|
||||
|
||||
export const identityAwsAuthServiceFactory = ({
|
||||
identityDAL,
|
||||
identityAccessTokenDAL,
|
||||
identityAwsAuthDAL,
|
||||
membershipIdentityDAL,
|
||||
@@ -93,11 +102,8 @@ export const identityAwsAuthServiceFactory = ({
|
||||
throw new NotFoundError({ message: "AWS auth method not found for identity, did you configure AWS auth?" });
|
||||
}
|
||||
|
||||
const identityMembershipOrg = await membershipIdentityDAL.findOne({
|
||||
actorIdentityId: identityAwsAuth.identityId,
|
||||
scope: AccessScope.Organization
|
||||
});
|
||||
if (!identityMembershipOrg) throw new UnauthorizedError({ message: "Identity not attached to a organization" });
|
||||
const identity = await identityDAL.findById(identityAwsAuth.identityId);
|
||||
if (!identity) throw new UnauthorizedError({ message: "Identity not found" });
|
||||
|
||||
const headers: TAwsGetCallerIdentityHeaders = JSON.parse(Buffer.from(iamRequestHeaders, "base64").toString());
|
||||
const body: string = Buffer.from(iamRequestBody, "base64").toString();
|
||||
@@ -159,8 +165,8 @@ export const identityAwsAuthServiceFactory = ({
|
||||
}
|
||||
|
||||
const identityAccessToken = await identityAwsAuthDAL.transaction(async (tx) => {
|
||||
await membershipIdentityDAL.updateById(
|
||||
identityMembershipOrg.id,
|
||||
await membershipIdentityDAL.update(
|
||||
{ scope: AccessScope.Organization, scopeOrgId: identity.orgId, actorIdentityId: identity.id },
|
||||
{
|
||||
lastLoginAuthMethod: IdentityAuthMethod.AWS_AUTH,
|
||||
lastLoginTime: new Date()
|
||||
@@ -212,7 +218,7 @@ export const identityAwsAuthServiceFactory = ({
|
||||
}
|
||||
);
|
||||
|
||||
return { accessToken, identityAwsAuth, identityAccessToken, identityMembershipOrg };
|
||||
return { accessToken, identityAwsAuth, identityAccessToken, identity };
|
||||
};
|
||||
|
||||
const attachAwsAuth = async ({
|
||||
@@ -240,6 +246,9 @@ export const identityAwsAuthServiceFactory = ({
|
||||
identityId
|
||||
});
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
if (identityMembershipOrg.identity.identityOrgId !== actorOrgId) {
|
||||
throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" });
|
||||
}
|
||||
|
||||
if (identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.AWS_AUTH)) {
|
||||
throw new BadRequestError({
|
||||
@@ -251,13 +260,14 @@ export const identityAwsAuthServiceFactory = ({
|
||||
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
actorOrgId,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Create, OrgPermissionSubjects.Identity);
|
||||
|
||||
const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId);
|
||||
@@ -320,6 +330,9 @@ export const identityAwsAuthServiceFactory = ({
|
||||
identityId
|
||||
});
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
if (identityMembershipOrg.identity.identityOrgId !== actorOrgId) {
|
||||
throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" });
|
||||
}
|
||||
|
||||
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.AWS_AUTH)) {
|
||||
throw new NotFoundError({
|
||||
@@ -336,13 +349,14 @@ export const identityAwsAuthServiceFactory = ({
|
||||
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
|
||||
const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId);
|
||||
@@ -387,6 +401,9 @@ export const identityAwsAuthServiceFactory = ({
|
||||
identityId
|
||||
});
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
if (identityMembershipOrg.identity.identityOrgId !== actorOrgId) {
|
||||
throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" });
|
||||
}
|
||||
|
||||
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.AWS_AUTH)) {
|
||||
throw new BadRequestError({
|
||||
@@ -396,13 +413,14 @@ export const identityAwsAuthServiceFactory = ({
|
||||
|
||||
const awsIdentityAuth = await identityAwsAuthDAL.findOne({ identityId });
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity);
|
||||
return { ...awsIdentityAuth, orgId: identityMembershipOrg.scopeOrgId };
|
||||
};
|
||||
@@ -422,27 +440,32 @@ export const identityAwsAuthServiceFactory = ({
|
||||
identityId
|
||||
});
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
if (identityMembershipOrg.identity.identityOrgId !== actorOrgId) {
|
||||
throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" });
|
||||
}
|
||||
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.AWS_AUTH)) {
|
||||
throw new BadRequestError({
|
||||
message: "The identity does not have aws auth"
|
||||
});
|
||||
}
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission(
|
||||
ActorType.IDENTITY,
|
||||
identityMembershipOrg.identity.id,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor: ActorType.IDENTITY,
|
||||
actorId: identityMembershipOrg.identity.id,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
|
||||
const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId);
|
||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
|
||||
import { AccessScope, IdentityAuthMethod } from "@app/db/schemas";
|
||||
import { AccessScope, IdentityAuthMethod, OrganizationActionScope } from "@app/db/schemas";
|
||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||
import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
|
||||
import {
|
||||
@@ -10,10 +10,17 @@ import {
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { crypto } from "@app/lib/crypto";
|
||||
import { BadRequestError, NotFoundError, PermissionBoundaryError, UnauthorizedError } from "@app/lib/errors";
|
||||
import {
|
||||
BadRequestError,
|
||||
ForbiddenRequestError,
|
||||
NotFoundError,
|
||||
PermissionBoundaryError,
|
||||
UnauthorizedError
|
||||
} from "@app/lib/errors";
|
||||
import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
|
||||
|
||||
import { ActorType, AuthTokenType } from "../auth/auth-type";
|
||||
import { TIdentityDALFactory } from "../identity/identity-dal";
|
||||
import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal";
|
||||
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
|
||||
import { TMembershipIdentityDALFactory } from "../membership-identity/membership-identity-dal";
|
||||
@@ -30,11 +37,12 @@ import {
|
||||
} from "./identity-azure-auth-types";
|
||||
|
||||
type TIdentityAzureAuthServiceFactoryDep = {
|
||||
identityDAL: Pick<TIdentityDALFactory, "findById">;
|
||||
identityAzureAuthDAL: Pick<
|
||||
TIdentityAzureAuthDALFactory,
|
||||
"findOne" | "transaction" | "create" | "updateById" | "delete"
|
||||
>;
|
||||
membershipIdentityDAL: Pick<TMembershipIdentityDALFactory, "findOne" | "updateById" | "getIdentityById">;
|
||||
membershipIdentityDAL: Pick<TMembershipIdentityDALFactory, "findOne" | "update" | "getIdentityById">;
|
||||
identityAccessTokenDAL: Pick<TIdentityAccessTokenDALFactory, "create" | "delete">;
|
||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||
@@ -44,6 +52,7 @@ type TIdentityAzureAuthServiceFactoryDep = {
|
||||
export type TIdentityAzureAuthServiceFactory = ReturnType<typeof identityAzureAuthServiceFactory>;
|
||||
|
||||
export const identityAzureAuthServiceFactory = ({
|
||||
identityDAL,
|
||||
identityAzureAuthDAL,
|
||||
membershipIdentityDAL,
|
||||
identityAccessTokenDAL,
|
||||
@@ -57,11 +66,8 @@ export const identityAzureAuthServiceFactory = ({
|
||||
throw new NotFoundError({ message: "Azure auth method not found for identity, did you configure Azure Auth?" });
|
||||
}
|
||||
|
||||
const identityMembershipOrg = await membershipIdentityDAL.findOne({
|
||||
actorIdentityId: identityAzureAuth.identityId,
|
||||
scope: AccessScope.Organization
|
||||
});
|
||||
if (!identityMembershipOrg) throw new UnauthorizedError({ message: "Identity not attached to a organization" });
|
||||
const identity = await identityDAL.findById(identityAzureAuth.identityId);
|
||||
if (!identity) throw new UnauthorizedError({ message: "Identity not found" });
|
||||
|
||||
const azureIdentity = await validateAzureIdentity({
|
||||
tenantId: identityAzureAuth.tenantId,
|
||||
@@ -86,8 +92,8 @@ export const identityAzureAuthServiceFactory = ({
|
||||
}
|
||||
|
||||
const identityAccessToken = await identityAzureAuthDAL.transaction(async (tx) => {
|
||||
await membershipIdentityDAL.updateById(
|
||||
identityMembershipOrg.id,
|
||||
await membershipIdentityDAL.update(
|
||||
{ scope: AccessScope.Organization, scopeOrgId: identity.orgId, actorIdentityId: identity.id },
|
||||
{
|
||||
lastLoginAuthMethod: IdentityAuthMethod.AZURE_AUTH,
|
||||
lastLoginTime: new Date()
|
||||
@@ -125,7 +131,7 @@ export const identityAzureAuthServiceFactory = ({
|
||||
}
|
||||
);
|
||||
|
||||
return { accessToken, identityAzureAuth, identityAccessToken, identityMembershipOrg };
|
||||
return { accessToken, identityAzureAuth, identityAccessToken, identity };
|
||||
};
|
||||
|
||||
const attachAzureAuth = async ({
|
||||
@@ -153,6 +159,9 @@ export const identityAzureAuthServiceFactory = ({
|
||||
identityId
|
||||
});
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
if (identityMembershipOrg.identity.identityOrgId !== actorOrgId) {
|
||||
throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" });
|
||||
}
|
||||
|
||||
if (identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.AZURE_AUTH)) {
|
||||
throw new BadRequestError({
|
||||
@@ -163,13 +172,14 @@ export const identityAzureAuthServiceFactory = ({
|
||||
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Create, OrgPermissionSubjects.Identity);
|
||||
|
||||
const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId);
|
||||
@@ -232,6 +242,9 @@ export const identityAzureAuthServiceFactory = ({
|
||||
identityId
|
||||
});
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
if (identityMembershipOrg.identity.identityOrgId !== actorOrgId) {
|
||||
throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" });
|
||||
}
|
||||
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.AZURE_AUTH)) {
|
||||
throw new BadRequestError({
|
||||
message: "Failed to update Azure Auth"
|
||||
@@ -247,13 +260,14 @@ export const identityAzureAuthServiceFactory = ({
|
||||
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
|
||||
const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId);
|
||||
@@ -301,6 +315,9 @@ export const identityAzureAuthServiceFactory = ({
|
||||
identityId
|
||||
});
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
if (identityMembershipOrg.identity.identityOrgId !== actorOrgId) {
|
||||
throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" });
|
||||
}
|
||||
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.AZURE_AUTH)) {
|
||||
throw new BadRequestError({
|
||||
message: "The identity does not have Azure Auth attached"
|
||||
@@ -309,13 +326,14 @@ export const identityAzureAuthServiceFactory = ({
|
||||
|
||||
const identityAzureAuth = await identityAzureAuthDAL.findOne({ identityId });
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity);
|
||||
|
||||
return { ...identityAzureAuth, orgId: identityMembershipOrg.scopeOrgId };
|
||||
@@ -336,27 +354,32 @@ export const identityAzureAuthServiceFactory = ({
|
||||
identityId
|
||||
});
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
if (identityMembershipOrg.identity.identityOrgId !== actorOrgId) {
|
||||
throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" });
|
||||
}
|
||||
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.AZURE_AUTH)) {
|
||||
throw new BadRequestError({
|
||||
message: "The identity does not have azure auth"
|
||||
});
|
||||
}
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission(
|
||||
ActorType.IDENTITY,
|
||||
identityMembershipOrg.identity.id,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor: ActorType.IDENTITY,
|
||||
actorId: identityMembershipOrg.identity.id,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId);
|
||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||
shouldUseNewPrivilegeSystem,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
|
||||
import { AccessScope, IdentityAuthMethod } from "@app/db/schemas";
|
||||
import { AccessScope, IdentityAuthMethod, OrganizationActionScope } from "@app/db/schemas";
|
||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||
import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
|
||||
import {
|
||||
@@ -10,10 +10,17 @@ import {
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { crypto } from "@app/lib/crypto";
|
||||
import { BadRequestError, NotFoundError, PermissionBoundaryError, UnauthorizedError } from "@app/lib/errors";
|
||||
import {
|
||||
BadRequestError,
|
||||
ForbiddenRequestError,
|
||||
NotFoundError,
|
||||
PermissionBoundaryError,
|
||||
UnauthorizedError
|
||||
} from "@app/lib/errors";
|
||||
import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
|
||||
|
||||
import { ActorType, AuthTokenType } from "../auth/auth-type";
|
||||
import { TIdentityDALFactory } from "../identity/identity-dal";
|
||||
import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal";
|
||||
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
|
||||
import { TMembershipIdentityDALFactory } from "../membership-identity/membership-identity-dal";
|
||||
@@ -31,8 +38,9 @@ import {
|
||||
} from "./identity-gcp-auth-types";
|
||||
|
||||
type TIdentityGcpAuthServiceFactoryDep = {
|
||||
identityDAL: Pick<TIdentityDALFactory, "findById">;
|
||||
identityGcpAuthDAL: Pick<TIdentityGcpAuthDALFactory, "findOne" | "transaction" | "create" | "updateById" | "delete">;
|
||||
membershipIdentityDAL: Pick<TMembershipIdentityDALFactory, "findOne" | "updateById" | "getIdentityById">;
|
||||
membershipIdentityDAL: Pick<TMembershipIdentityDALFactory, "findOne" | "update" | "getIdentityById">;
|
||||
identityAccessTokenDAL: Pick<TIdentityAccessTokenDALFactory, "create" | "delete">;
|
||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||
@@ -42,6 +50,7 @@ type TIdentityGcpAuthServiceFactoryDep = {
|
||||
export type TIdentityGcpAuthServiceFactory = ReturnType<typeof identityGcpAuthServiceFactory>;
|
||||
|
||||
export const identityGcpAuthServiceFactory = ({
|
||||
identityDAL,
|
||||
identityGcpAuthDAL,
|
||||
membershipIdentityDAL,
|
||||
identityAccessTokenDAL,
|
||||
@@ -55,13 +64,8 @@ export const identityGcpAuthServiceFactory = ({
|
||||
throw new NotFoundError({ message: "GCP auth method not found for identity, did you configure GCP auth?" });
|
||||
}
|
||||
|
||||
const identityMembershipOrg = await membershipIdentityDAL.findOne({
|
||||
actorIdentityId: identityGcpAuth.identityId,
|
||||
scope: AccessScope.Organization
|
||||
});
|
||||
if (!identityMembershipOrg) {
|
||||
throw new UnauthorizedError({ message: "Identity does not belong to any organization" });
|
||||
}
|
||||
const identity = await identityDAL.findById(identityGcpAuth.identityId);
|
||||
if (!identity) throw new UnauthorizedError({ message: "Identity not found" });
|
||||
|
||||
let gcpIdentityDetails: TGcpIdentityDetails;
|
||||
switch (identityGcpAuth.type) {
|
||||
@@ -125,8 +129,8 @@ export const identityGcpAuthServiceFactory = ({
|
||||
}
|
||||
|
||||
const identityAccessToken = await identityGcpAuthDAL.transaction(async (tx) => {
|
||||
await membershipIdentityDAL.updateById(
|
||||
identityMembershipOrg.id,
|
||||
await membershipIdentityDAL.update(
|
||||
{ scope: AccessScope.Organization, scopeOrgId: identity.orgId, actorIdentityId: identity.id },
|
||||
{
|
||||
lastLoginAuthMethod: IdentityAuthMethod.GCP_AUTH,
|
||||
lastLoginTime: new Date()
|
||||
@@ -164,7 +168,7 @@ export const identityGcpAuthServiceFactory = ({
|
||||
}
|
||||
);
|
||||
|
||||
return { accessToken, identityGcpAuth, identityAccessToken, identityMembershipOrg };
|
||||
return { accessToken, identityGcpAuth, identityAccessToken, identity };
|
||||
};
|
||||
|
||||
const attachGcpAuth = async ({
|
||||
@@ -193,6 +197,9 @@ export const identityGcpAuthServiceFactory = ({
|
||||
identityId
|
||||
});
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
if (identityMembershipOrg.identity.identityOrgId !== actorOrgId) {
|
||||
throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" });
|
||||
}
|
||||
|
||||
if (identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.GCP_AUTH)) {
|
||||
throw new BadRequestError({
|
||||
@@ -204,13 +211,14 @@ export const identityGcpAuthServiceFactory = ({
|
||||
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Create, OrgPermissionSubjects.Identity);
|
||||
|
||||
const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId);
|
||||
@@ -274,6 +282,9 @@ export const identityGcpAuthServiceFactory = ({
|
||||
identityId
|
||||
});
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
if (identityMembershipOrg.identity.identityOrgId !== actorOrgId) {
|
||||
throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" });
|
||||
}
|
||||
|
||||
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.GCP_AUTH)) {
|
||||
throw new BadRequestError({
|
||||
@@ -290,13 +301,14 @@ export const identityGcpAuthServiceFactory = ({
|
||||
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
|
||||
const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId);
|
||||
@@ -345,6 +357,9 @@ export const identityGcpAuthServiceFactory = ({
|
||||
identityId
|
||||
});
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
if (identityMembershipOrg.identity.identityOrgId !== actorOrgId) {
|
||||
throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" });
|
||||
}
|
||||
|
||||
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.GCP_AUTH)) {
|
||||
throw new BadRequestError({
|
||||
@@ -354,13 +369,14 @@ export const identityGcpAuthServiceFactory = ({
|
||||
|
||||
const identityGcpAuth = await identityGcpAuthDAL.findOne({ identityId });
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity);
|
||||
|
||||
return { ...identityGcpAuth, orgId: identityMembershipOrg.scopeOrgId };
|
||||
@@ -381,28 +397,33 @@ export const identityGcpAuthServiceFactory = ({
|
||||
identityId
|
||||
});
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
if (identityMembershipOrg.identity.identityOrgId !== actorOrgId) {
|
||||
throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" });
|
||||
}
|
||||
|
||||
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.GCP_AUTH)) {
|
||||
throw new BadRequestError({
|
||||
message: "The identity does not have gcp auth"
|
||||
});
|
||||
}
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission(
|
||||
ActorType.IDENTITY,
|
||||
identityMembershipOrg.identity.id,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor: ActorType.IDENTITY,
|
||||
actorId: identityMembershipOrg.identity.id,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId);
|
||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||
shouldUseNewPrivilegeSystem,
|
||||
|
||||
@@ -3,7 +3,7 @@ import https from "https";
|
||||
import jwt from "jsonwebtoken";
|
||||
import { JwksClient } from "jwks-rsa";
|
||||
|
||||
import { AccessScope, IdentityAuthMethod, TIdentityJwtAuthsUpdate } from "@app/db/schemas";
|
||||
import { AccessScope, IdentityAuthMethod, OrganizationActionScope, TIdentityJwtAuthsUpdate } from "@app/db/schemas";
|
||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||
import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
|
||||
import {
|
||||
@@ -24,6 +24,7 @@ import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
|
||||
import { getValueByDot } from "@app/lib/template/dot-access";
|
||||
|
||||
import { ActorType, AuthTokenType } from "../auth/auth-type";
|
||||
import { TIdentityDALFactory } from "../identity/identity-dal";
|
||||
import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal";
|
||||
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
|
||||
import { TKmsServiceFactory } from "../kms/kms-service";
|
||||
@@ -43,8 +44,9 @@ import {
|
||||
} from "./identity-jwt-auth-types";
|
||||
|
||||
type TIdentityJwtAuthServiceFactoryDep = {
|
||||
identityDAL: Pick<TIdentityDALFactory, "findById">;
|
||||
identityJwtAuthDAL: TIdentityJwtAuthDALFactory;
|
||||
membershipIdentityDAL: Pick<TMembershipIdentityDALFactory, "findOne" | "updateById" | "getIdentityById">;
|
||||
membershipIdentityDAL: Pick<TMembershipIdentityDALFactory, "findOne" | "update" | "getIdentityById">;
|
||||
identityAccessTokenDAL: Pick<TIdentityAccessTokenDALFactory, "create" | "delete">;
|
||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||
@@ -55,6 +57,7 @@ type TIdentityJwtAuthServiceFactoryDep = {
|
||||
export type TIdentityJwtAuthServiceFactory = ReturnType<typeof identityJwtAuthServiceFactory>;
|
||||
|
||||
export const identityJwtAuthServiceFactory = ({
|
||||
identityDAL,
|
||||
identityJwtAuthDAL,
|
||||
membershipIdentityDAL,
|
||||
permissionService,
|
||||
@@ -69,19 +72,12 @@ export const identityJwtAuthServiceFactory = ({
|
||||
throw new NotFoundError({ message: "JWT auth method not found for identity, did you configure JWT auth?" });
|
||||
}
|
||||
|
||||
const identityMembershipOrg = await membershipIdentityDAL.findOne({
|
||||
actorIdentityId: identityJwtAuth.identityId,
|
||||
scope: AccessScope.Organization
|
||||
});
|
||||
if (!identityMembershipOrg) {
|
||||
throw new NotFoundError({
|
||||
message: `Identity organization membership for identity with ID '${identityJwtAuth.identityId}' not found`
|
||||
});
|
||||
}
|
||||
const identity = await identityDAL.findById(identityJwtAuth.identityId);
|
||||
if (!identity) throw new UnauthorizedError({ message: "Identity not found" });
|
||||
|
||||
const { decryptor: orgDataKeyDecryptor } = await kmsService.createCipherPairWithDataKey({
|
||||
type: KmsDataKey.Organization,
|
||||
orgId: identityMembershipOrg.scopeOrgId
|
||||
orgId: identity.orgId
|
||||
});
|
||||
|
||||
const decodedToken = crypto.jwt().decode(jwtValue, { complete: true });
|
||||
@@ -211,12 +207,9 @@ export const identityJwtAuthServiceFactory = ({
|
||||
}
|
||||
|
||||
const identityAccessToken = await identityJwtAuthDAL.transaction(async (tx) => {
|
||||
await membershipIdentityDAL.updateById(
|
||||
identityMembershipOrg.id,
|
||||
{
|
||||
lastLoginAuthMethod: IdentityAuthMethod.JWT_AUTH,
|
||||
lastLoginTime: new Date()
|
||||
},
|
||||
await membershipIdentityDAL.update(
|
||||
{ scope: AccessScope.Organization, scopeOrgId: identity.orgId, actorIdentityId: identity.id },
|
||||
{ lastLoginAuthMethod: IdentityAuthMethod.JWT_AUTH, lastLoginTime: new Date() },
|
||||
tx
|
||||
);
|
||||
const newToken = await identityAccessTokenDAL.create(
|
||||
@@ -251,7 +244,7 @@ export const identityJwtAuthServiceFactory = ({
|
||||
}
|
||||
);
|
||||
|
||||
return { accessToken, identityJwtAuth, identityAccessToken, identityMembershipOrg };
|
||||
return { accessToken, identityJwtAuth, identityAccessToken, identity };
|
||||
};
|
||||
|
||||
const attachJwtAuth = async ({
|
||||
@@ -284,6 +277,9 @@ export const identityJwtAuthServiceFactory = ({
|
||||
identityId
|
||||
});
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
if (identityMembershipOrg.identity.identityOrgId !== actorOrgId) {
|
||||
throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" });
|
||||
}
|
||||
if (identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.JWT_AUTH)) {
|
||||
throw new BadRequestError({
|
||||
message: "Failed to add JWT Auth to already configured identity"
|
||||
@@ -294,13 +290,14 @@ export const identityJwtAuthServiceFactory = ({
|
||||
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Create, OrgPermissionSubjects.Identity);
|
||||
|
||||
@@ -387,6 +384,9 @@ export const identityJwtAuthServiceFactory = ({
|
||||
identityId
|
||||
});
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
if (identityMembershipOrg.identity.identityOrgId !== actorOrgId) {
|
||||
throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" });
|
||||
}
|
||||
|
||||
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.JWT_AUTH)) {
|
||||
throw new BadRequestError({
|
||||
@@ -403,13 +403,14 @@ export const identityJwtAuthServiceFactory = ({
|
||||
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
|
||||
@@ -491,6 +492,9 @@ export const identityJwtAuthServiceFactory = ({
|
||||
identityId
|
||||
});
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
if (identityMembershipOrg.identity.identityOrgId !== actorOrgId) {
|
||||
throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" });
|
||||
}
|
||||
|
||||
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.JWT_AUTH)) {
|
||||
throw new BadRequestError({
|
||||
@@ -498,13 +502,14 @@ export const identityJwtAuthServiceFactory = ({
|
||||
});
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity);
|
||||
|
||||
@@ -539,6 +544,9 @@ export const identityJwtAuthServiceFactory = ({
|
||||
if (!identityMembershipOrg) {
|
||||
throw new NotFoundError({ message: "Failed to find identity" });
|
||||
}
|
||||
if (identityMembershipOrg.identity.identityOrgId !== actorOrgId) {
|
||||
throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" });
|
||||
}
|
||||
|
||||
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.JWT_AUTH)) {
|
||||
throw new BadRequestError({
|
||||
@@ -546,23 +554,25 @@ export const identityJwtAuthServiceFactory = ({
|
||||
});
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission(
|
||||
ActorType.IDENTITY,
|
||||
identityMembershipOrg.identity.id,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor: ActorType.IDENTITY,
|
||||
actorId: identityMembershipOrg.identity.id,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
|
||||
const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId);
|
||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||
|
||||
@@ -3,7 +3,12 @@ import axios, { AxiosError } from "axios";
|
||||
import https from "https";
|
||||
import RE2 from "re2";
|
||||
|
||||
import { AccessScope, IdentityAuthMethod, TIdentityKubernetesAuthsUpdate } from "@app/db/schemas";
|
||||
import {
|
||||
AccessScope,
|
||||
IdentityAuthMethod,
|
||||
OrganizationActionScope,
|
||||
TIdentityKubernetesAuthsUpdate
|
||||
} from "@app/db/schemas";
|
||||
import { TGatewayDALFactory } from "@app/ee/services/gateway/gateway-dal";
|
||||
import { TGatewayServiceFactory } from "@app/ee/services/gateway/gateway-service";
|
||||
import { TGatewayV2DALFactory } from "@app/ee/services/gateway-v2/gateway-v2-dal";
|
||||
@@ -21,13 +26,20 @@ import {
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { crypto } from "@app/lib/crypto";
|
||||
import { BadRequestError, NotFoundError, PermissionBoundaryError, UnauthorizedError } from "@app/lib/errors";
|
||||
import {
|
||||
BadRequestError,
|
||||
ForbiddenRequestError,
|
||||
NotFoundError,
|
||||
PermissionBoundaryError,
|
||||
UnauthorizedError
|
||||
} from "@app/lib/errors";
|
||||
import { GatewayHttpProxyActions, GatewayProxyProtocol, withGatewayProxy } from "@app/lib/gateway";
|
||||
import { withGatewayV2Proxy } from "@app/lib/gateway-v2/gateway-v2";
|
||||
import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
|
||||
import { logger } from "@app/lib/logger";
|
||||
|
||||
import { ActorType, AuthTokenType } from "../auth/auth-type";
|
||||
import { TIdentityDALFactory } from "../identity/identity-dal";
|
||||
import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal";
|
||||
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
|
||||
import { TKmsServiceFactory } from "../kms/kms-service";
|
||||
@@ -48,12 +60,13 @@ import {
|
||||
} from "./identity-kubernetes-auth-types";
|
||||
|
||||
type TIdentityKubernetesAuthServiceFactoryDep = {
|
||||
identityDAL: Pick<TIdentityDALFactory, "findById">;
|
||||
identityKubernetesAuthDAL: Pick<
|
||||
TIdentityKubernetesAuthDALFactory,
|
||||
"create" | "findOne" | "transaction" | "updateById" | "delete"
|
||||
>;
|
||||
identityAccessTokenDAL: Pick<TIdentityAccessTokenDALFactory, "create" | "delete">;
|
||||
membershipIdentityDAL: Pick<TMembershipIdentityDALFactory, "findOne" | "updateById" | "getIdentityById">;
|
||||
membershipIdentityDAL: Pick<TMembershipIdentityDALFactory, "findOne" | "update" | "getIdentityById">;
|
||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
|
||||
@@ -69,6 +82,7 @@ export type TIdentityKubernetesAuthServiceFactory = ReturnType<typeof identityKu
|
||||
const GATEWAY_AUTH_DEFAULT_HOST = "https://kubernetes.default.svc.cluster.local";
|
||||
|
||||
export const identityKubernetesAuthServiceFactory = ({
|
||||
identityDAL,
|
||||
identityKubernetesAuthDAL,
|
||||
membershipIdentityDAL,
|
||||
identityAccessTokenDAL,
|
||||
@@ -175,19 +189,12 @@ export const identityKubernetesAuthServiceFactory = ({
|
||||
});
|
||||
}
|
||||
|
||||
const identityMembershipOrg = await membershipIdentityDAL.findOne({
|
||||
actorIdentityId: identityKubernetesAuth.identityId,
|
||||
scope: AccessScope.Organization
|
||||
});
|
||||
if (!identityMembershipOrg) {
|
||||
throw new NotFoundError({
|
||||
message: `Identity organization membership for identity with ID '${identityKubernetesAuth.identityId}' not found`
|
||||
});
|
||||
}
|
||||
const identity = await identityDAL.findById(identityKubernetesAuth.identityId);
|
||||
if (!identity) throw new UnauthorizedError({ message: "Identity not found" });
|
||||
|
||||
const { decryptor } = await kmsService.createCipherPairWithDataKey({
|
||||
type: KmsDataKey.Organization,
|
||||
orgId: identityMembershipOrg.scopeOrgId
|
||||
orgId: identity.orgId
|
||||
});
|
||||
|
||||
let caCert = "";
|
||||
@@ -430,12 +437,9 @@ export const identityKubernetesAuthServiceFactory = ({
|
||||
}
|
||||
|
||||
const identityAccessToken = await identityKubernetesAuthDAL.transaction(async (tx) => {
|
||||
await membershipIdentityDAL.updateById(
|
||||
identityMembershipOrg.id,
|
||||
{
|
||||
lastLoginAuthMethod: IdentityAuthMethod.KUBERNETES_AUTH,
|
||||
lastLoginTime: new Date()
|
||||
},
|
||||
await membershipIdentityDAL.update(
|
||||
{ scope: AccessScope.Organization, scopeOrgId: identity.orgId, actorIdentityId: identity.id },
|
||||
{ lastLoginAuthMethod: IdentityAuthMethod.KUBERNETES_AUTH, lastLoginTime: new Date() },
|
||||
tx
|
||||
);
|
||||
const newToken = await identityAccessTokenDAL.create(
|
||||
@@ -475,7 +479,7 @@ export const identityKubernetesAuthServiceFactory = ({
|
||||
}
|
||||
);
|
||||
|
||||
return { accessToken, identityKubernetesAuth, identityAccessToken, identityMembershipOrg };
|
||||
return { accessToken, identityKubernetesAuth, identityAccessToken, identity };
|
||||
};
|
||||
|
||||
const attachKubernetesAuth = async ({
|
||||
@@ -508,6 +512,9 @@ export const identityKubernetesAuthServiceFactory = ({
|
||||
identityId
|
||||
});
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
if (identityMembershipOrg.identity.identityOrgId !== actorOrgId) {
|
||||
throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" });
|
||||
}
|
||||
|
||||
if (identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.KUBERNETES_AUTH)) {
|
||||
throw new BadRequestError({
|
||||
@@ -519,13 +526,14 @@ export const identityKubernetesAuthServiceFactory = ({
|
||||
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Create, OrgPermissionSubjects.Identity);
|
||||
|
||||
const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId);
|
||||
@@ -560,13 +568,14 @@ export const identityKubernetesAuthServiceFactory = ({
|
||||
isGatewayV1 = false;
|
||||
}
|
||||
|
||||
const { permission: orgPermission } = await permissionService.getOrgPermission(
|
||||
const { permission: orgPermission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
ForbiddenError.from(orgPermission).throwUnlessCan(
|
||||
OrgPermissionGatewayActions.AttachGateways,
|
||||
OrgPermissionSubjects.Gateway
|
||||
@@ -633,6 +642,9 @@ export const identityKubernetesAuthServiceFactory = ({
|
||||
identityId
|
||||
});
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
if (identityMembershipOrg.identity.identityOrgId !== actorOrgId) {
|
||||
throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" });
|
||||
}
|
||||
|
||||
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.KUBERNETES_AUTH)) {
|
||||
throw new BadRequestError({
|
||||
@@ -650,13 +662,14 @@ export const identityKubernetesAuthServiceFactory = ({
|
||||
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
|
||||
const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId);
|
||||
@@ -692,13 +705,14 @@ export const identityKubernetesAuthServiceFactory = ({
|
||||
isGatewayV1 = false;
|
||||
}
|
||||
|
||||
const { permission: orgPermission } = await permissionService.getOrgPermission(
|
||||
const { permission: orgPermission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
ForbiddenError.from(orgPermission).throwUnlessCan(
|
||||
OrgPermissionGatewayActions.AttachGateways,
|
||||
OrgPermissionSubjects.Gateway
|
||||
@@ -779,6 +793,9 @@ export const identityKubernetesAuthServiceFactory = ({
|
||||
identityId
|
||||
});
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
if (identityMembershipOrg.identity.identityOrgId !== actorOrgId) {
|
||||
throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" });
|
||||
}
|
||||
|
||||
const identityKubernetesAuth = await identityKubernetesAuthDAL.findOne({ identityId });
|
||||
if (!identityKubernetesAuth) {
|
||||
@@ -791,13 +808,14 @@ export const identityKubernetesAuthServiceFactory = ({
|
||||
});
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity);
|
||||
|
||||
const { decryptor } = await kmsService.createCipherPairWithDataKey({
|
||||
@@ -841,28 +859,33 @@ export const identityKubernetesAuthServiceFactory = ({
|
||||
identityId
|
||||
});
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
if (identityMembershipOrg.identity.identityOrgId !== actorOrgId) {
|
||||
throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" });
|
||||
}
|
||||
|
||||
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.KUBERNETES_AUTH)) {
|
||||
throw new BadRequestError({
|
||||
message: "The identity does not have kubernetes auth"
|
||||
});
|
||||
}
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission(
|
||||
ActorType.IDENTITY,
|
||||
identityMembershipOrg.identity.id,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission({
|
||||
actor: ActorType.IDENTITY,
|
||||
actorId: identityMembershipOrg.identity.id,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
actorOrgId,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId);
|
||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||
shouldUseNewPrivilegeSystem,
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
import slugify from "@sindresorhus/slugify";
|
||||
|
||||
import { AccessScope, IdentityAuthMethod } from "@app/db/schemas";
|
||||
import { AccessScope, IdentityAuthMethod, OrganizationActionScope } from "@app/db/schemas";
|
||||
import { TIdentityAuthTemplateDALFactory } from "@app/ee/services/identity-auth-template";
|
||||
import { testLDAPConfig } from "@app/ee/services/ldap-config/ldap-fns";
|
||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||
@@ -21,6 +21,7 @@ import { getConfig } from "@app/lib/config/env";
|
||||
import { crypto } from "@app/lib/crypto";
|
||||
import {
|
||||
BadRequestError,
|
||||
ForbiddenRequestError,
|
||||
NotFoundError,
|
||||
PermissionBoundaryError,
|
||||
RateLimitError,
|
||||
@@ -56,11 +57,11 @@ type TIdentityLdapAuthServiceFactoryDep = {
|
||||
TIdentityLdapAuthDALFactory,
|
||||
"findOne" | "transaction" | "create" | "updateById" | "delete"
|
||||
>;
|
||||
membershipIdentityDAL: Pick<TMembershipIdentityDALFactory, "findOne" | "updateById" | "getIdentityById">;
|
||||
membershipIdentityDAL: Pick<TMembershipIdentityDALFactory, "findOne" | "update" | "getIdentityById">;
|
||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
||||
kmsService: TKmsServiceFactory;
|
||||
identityDAL: TIdentityDALFactory;
|
||||
identityDAL: Pick<TIdentityDALFactory, "findById" | "findOne">;
|
||||
identityAuthTemplateDAL: TIdentityAuthTemplateDALFactory;
|
||||
keyStore: Pick<
|
||||
TKeyStoreFactory,
|
||||
@@ -150,17 +151,6 @@ export const identityLdapAuthServiceFactory = ({
|
||||
};
|
||||
|
||||
const login = async ({ identityId }: TLoginLdapAuthDTO) => {
|
||||
const identityMembershipOrg = await membershipIdentityDAL.findOne({
|
||||
actorIdentityId: identityId,
|
||||
scope: AccessScope.Organization
|
||||
});
|
||||
|
||||
if (!identityMembershipOrg) {
|
||||
throw new UnauthorizedError({
|
||||
message: "Invalid credentials"
|
||||
});
|
||||
}
|
||||
|
||||
const identityLdapAuth = await identityLdapAuthDAL.findOne({ identityId });
|
||||
|
||||
if (!identityLdapAuth) {
|
||||
@@ -169,7 +159,10 @@ export const identityLdapAuthServiceFactory = ({
|
||||
});
|
||||
}
|
||||
|
||||
const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId);
|
||||
const identity = await identityDAL.findById(identityLdapAuth.identityId);
|
||||
if (!identity) throw new UnauthorizedError({ message: "Identity not found" });
|
||||
|
||||
const plan = await licenseService.getPlan(identity.orgId);
|
||||
if (!plan.ldap) {
|
||||
throw new BadRequestError({
|
||||
message:
|
||||
@@ -178,12 +171,9 @@ export const identityLdapAuthServiceFactory = ({
|
||||
}
|
||||
|
||||
const identityAccessToken = await identityLdapAuthDAL.transaction(async (tx) => {
|
||||
await membershipIdentityDAL.updateById(
|
||||
identityMembershipOrg.id,
|
||||
{
|
||||
lastLoginAuthMethod: IdentityAuthMethod.LDAP_AUTH,
|
||||
lastLoginTime: new Date()
|
||||
},
|
||||
await membershipIdentityDAL.update(
|
||||
{ scope: AccessScope.Organization, scopeOrgId: identity.orgId, actorIdentityId: identity.id },
|
||||
{ lastLoginAuthMethod: IdentityAuthMethod.LDAP_AUTH, lastLoginTime: new Date() },
|
||||
tx
|
||||
);
|
||||
const newToken = await identityAccessTokenDAL.create(
|
||||
@@ -217,7 +207,7 @@ export const identityLdapAuthServiceFactory = ({
|
||||
}
|
||||
);
|
||||
|
||||
return { accessToken, identityLdapAuth, identityAccessToken, identityMembershipOrg };
|
||||
return { accessToken, identityLdapAuth, identityAccessToken, identity };
|
||||
};
|
||||
|
||||
const attachLdapAuth = async ({
|
||||
@@ -254,6 +244,9 @@ export const identityLdapAuthServiceFactory = ({
|
||||
identityId
|
||||
});
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
if (identityMembershipOrg.identity.identityOrgId !== actorOrgId) {
|
||||
throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" });
|
||||
}
|
||||
|
||||
if (identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.LDAP_AUTH)) {
|
||||
throw new BadRequestError({
|
||||
@@ -265,13 +258,14 @@ export const identityLdapAuthServiceFactory = ({
|
||||
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Create, OrgPermissionSubjects.Identity);
|
||||
|
||||
if (templateId) {
|
||||
@@ -425,6 +419,9 @@ export const identityLdapAuthServiceFactory = ({
|
||||
identityId
|
||||
});
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
if (identityMembershipOrg.identity.identityOrgId !== actorOrgId) {
|
||||
throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" });
|
||||
}
|
||||
|
||||
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.LDAP_AUTH)) {
|
||||
throw new NotFoundError({
|
||||
@@ -441,13 +438,14 @@ export const identityLdapAuthServiceFactory = ({
|
||||
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
|
||||
if (templateId) {
|
||||
@@ -588,6 +586,9 @@ export const identityLdapAuthServiceFactory = ({
|
||||
identityId
|
||||
});
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
if (identityMembershipOrg.identity.identityOrgId !== actorOrgId) {
|
||||
throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" });
|
||||
}
|
||||
|
||||
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.LDAP_AUTH)) {
|
||||
throw new BadRequestError({
|
||||
@@ -597,13 +598,14 @@ export const identityLdapAuthServiceFactory = ({
|
||||
|
||||
const ldapIdentityAuth = await identityLdapAuthDAL.findOne({ identityId });
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
|
||||
const { decryptor } = await kmsService.createCipherPairWithDataKey({
|
||||
type: KmsDataKey.Organization,
|
||||
@@ -635,27 +637,32 @@ export const identityLdapAuthServiceFactory = ({
|
||||
identityId
|
||||
});
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
if (identityMembershipOrg.identity.identityOrgId !== actorOrgId) {
|
||||
throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" });
|
||||
}
|
||||
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.LDAP_AUTH)) {
|
||||
throw new BadRequestError({
|
||||
message: "The identity does not have LDAP Auth attached"
|
||||
});
|
||||
}
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission(
|
||||
ActorType.IDENTITY,
|
||||
identityMembershipOrg.identity.id,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission({
|
||||
actor: ActorType.IDENTITY,
|
||||
actorId: identityMembershipOrg.identity.id,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
actorOrgId,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
|
||||
const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId);
|
||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||
@@ -785,13 +792,14 @@ export const identityLdapAuthServiceFactory = ({
|
||||
});
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
|
||||
const deleted = await keyStore.deleteItems({
|
||||
|
||||
@@ -3,7 +3,7 @@ import { ForbiddenError } from "@casl/ability";
|
||||
import { AxiosError } from "axios";
|
||||
import RE2 from "re2";
|
||||
|
||||
import { AccessScope, IdentityAuthMethod } from "@app/db/schemas";
|
||||
import { AccessScope, IdentityAuthMethod, OrganizationActionScope } from "@app/db/schemas";
|
||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||
import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
|
||||
import {
|
||||
@@ -14,11 +14,18 @@ import { TPermissionServiceFactory } from "@app/ee/services/permission/permissio
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { request } from "@app/lib/config/request";
|
||||
import { crypto } from "@app/lib/crypto";
|
||||
import { BadRequestError, NotFoundError, PermissionBoundaryError, UnauthorizedError } from "@app/lib/errors";
|
||||
import {
|
||||
BadRequestError,
|
||||
ForbiddenRequestError,
|
||||
NotFoundError,
|
||||
PermissionBoundaryError,
|
||||
UnauthorizedError
|
||||
} from "@app/lib/errors";
|
||||
import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
|
||||
import { logger } from "@app/lib/logger";
|
||||
|
||||
import { ActorType, AuthTokenType } from "../auth/auth-type";
|
||||
import { TIdentityDALFactory } from "../identity/identity-dal";
|
||||
import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal";
|
||||
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
|
||||
import { TMembershipIdentityDALFactory } from "../membership-identity/membership-identity-dal";
|
||||
@@ -35,9 +42,10 @@ import {
|
||||
} from "./identity-oci-auth-types";
|
||||
|
||||
type TIdentityOciAuthServiceFactoryDep = {
|
||||
identityDAL: Pick<TIdentityDALFactory, "findById">;
|
||||
identityAccessTokenDAL: Pick<TIdentityAccessTokenDALFactory, "create" | "delete">;
|
||||
identityOciAuthDAL: Pick<TIdentityOciAuthDALFactory, "findOne" | "transaction" | "create" | "updateById" | "delete">;
|
||||
membershipIdentityDAL: Pick<TMembershipIdentityDALFactory, "findOne" | "updateById" | "getIdentityById">;
|
||||
membershipIdentityDAL: Pick<TMembershipIdentityDALFactory, "findOne" | "update" | "getIdentityById">;
|
||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
||||
orgDAL: Pick<TOrgDALFactory, "findById">;
|
||||
@@ -46,6 +54,7 @@ type TIdentityOciAuthServiceFactoryDep = {
|
||||
export type TIdentityOciAuthServiceFactory = ReturnType<typeof identityOciAuthServiceFactory>;
|
||||
|
||||
export const identityOciAuthServiceFactory = ({
|
||||
identityDAL,
|
||||
identityAccessTokenDAL,
|
||||
identityOciAuthDAL,
|
||||
membershipIdentityDAL,
|
||||
@@ -59,11 +68,8 @@ export const identityOciAuthServiceFactory = ({
|
||||
throw new NotFoundError({ message: "OCI auth method not found for identity, did you configure OCI auth?" });
|
||||
}
|
||||
|
||||
const identityMembershipOrg = await membershipIdentityDAL.findOne({
|
||||
actorIdentityId: identityOciAuth.identityId,
|
||||
scope: AccessScope.Organization
|
||||
});
|
||||
if (!identityMembershipOrg) throw new UnauthorizedError({ message: "Identity not attached to a organization" });
|
||||
const identity = await identityDAL.findById(identityOciAuth.identityId);
|
||||
if (!identity) throw new UnauthorizedError({ message: "Identity not found" });
|
||||
|
||||
// Validate OCI host format. Ensures that the host is in "identity.<region>.oraclecloud.com" format.
|
||||
if (!headers.host || !new RE2("^identity\\.([a-z]{2}-[a-z]+-[1-9])\\.oraclecloud\\.com$").test(headers.host)) {
|
||||
@@ -98,12 +104,9 @@ export const identityOciAuthServiceFactory = ({
|
||||
|
||||
// Generate the token
|
||||
const identityAccessToken = await identityOciAuthDAL.transaction(async (tx) => {
|
||||
await membershipIdentityDAL.updateById(
|
||||
identityMembershipOrg.id,
|
||||
{
|
||||
lastLoginAuthMethod: IdentityAuthMethod.OCI_AUTH,
|
||||
lastLoginTime: new Date()
|
||||
},
|
||||
await membershipIdentityDAL.update(
|
||||
{ scope: AccessScope.Organization, scopeOrgId: identity.orgId, actorIdentityId: identity.id },
|
||||
{ lastLoginAuthMethod: IdentityAuthMethod.OCI_AUTH, lastLoginTime: new Date() },
|
||||
tx
|
||||
);
|
||||
const newToken = await identityAccessTokenDAL.create(
|
||||
@@ -140,7 +143,7 @@ export const identityOciAuthServiceFactory = ({
|
||||
identityOciAuth,
|
||||
accessToken,
|
||||
identityAccessToken,
|
||||
identityMembershipOrg
|
||||
identity
|
||||
};
|
||||
};
|
||||
|
||||
@@ -168,6 +171,9 @@ export const identityOciAuthServiceFactory = ({
|
||||
identityId
|
||||
});
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
if (identityMembershipOrg.identity.identityOrgId !== actorOrgId) {
|
||||
throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" });
|
||||
}
|
||||
|
||||
if (identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.OCI_AUTH)) {
|
||||
throw new BadRequestError({
|
||||
@@ -179,13 +185,14 @@ export const identityOciAuthServiceFactory = ({
|
||||
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Create, OrgPermissionSubjects.Identity);
|
||||
|
||||
const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId);
|
||||
@@ -246,6 +253,9 @@ export const identityOciAuthServiceFactory = ({
|
||||
identityId
|
||||
});
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
if (identityMembershipOrg.identity.identityOrgId !== actorOrgId) {
|
||||
throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" });
|
||||
}
|
||||
|
||||
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.OCI_AUTH)) {
|
||||
throw new NotFoundError({
|
||||
@@ -262,13 +272,14 @@ export const identityOciAuthServiceFactory = ({
|
||||
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
|
||||
const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId);
|
||||
@@ -312,6 +323,9 @@ export const identityOciAuthServiceFactory = ({
|
||||
identityId
|
||||
});
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
if (identityMembershipOrg.identity.identityOrgId !== actorOrgId) {
|
||||
throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" });
|
||||
}
|
||||
|
||||
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.OCI_AUTH)) {
|
||||
throw new BadRequestError({
|
||||
@@ -321,13 +335,14 @@ export const identityOciAuthServiceFactory = ({
|
||||
|
||||
const ociIdentityAuth = await identityOciAuthDAL.findOne({ identityId });
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity);
|
||||
return { ...ociIdentityAuth, orgId: identityMembershipOrg.scopeOrgId };
|
||||
};
|
||||
@@ -347,27 +362,32 @@ export const identityOciAuthServiceFactory = ({
|
||||
identityId
|
||||
});
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
if (identityMembershipOrg.identity.identityOrgId !== actorOrgId) {
|
||||
throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" });
|
||||
}
|
||||
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.OCI_AUTH)) {
|
||||
throw new BadRequestError({
|
||||
message: "The identity does not have OCI auth"
|
||||
});
|
||||
}
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission(
|
||||
ActorType.IDENTITY,
|
||||
identityMembershipOrg.identity.id,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission({
|
||||
actor: ActorType.IDENTITY,
|
||||
actorId: identityMembershipOrg.identity.id,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
actorOrgId,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
|
||||
const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(actorOrgId);
|
||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||
|
||||
@@ -4,7 +4,7 @@ import https from "https";
|
||||
import jwt from "jsonwebtoken";
|
||||
import { JwksClient } from "jwks-rsa";
|
||||
|
||||
import { AccessScope, IdentityAuthMethod, TIdentityOidcAuthsUpdate } from "@app/db/schemas";
|
||||
import { AccessScope, IdentityAuthMethod, OrganizationActionScope, TIdentityOidcAuthsUpdate } from "@app/db/schemas";
|
||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||
import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
|
||||
import {
|
||||
@@ -25,6 +25,7 @@ import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
|
||||
import { getValueByDot } from "@app/lib/template/dot-access";
|
||||
|
||||
import { ActorType, AuthTokenType } from "../auth/auth-type";
|
||||
import { TIdentityDALFactory } from "../identity/identity-dal";
|
||||
import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal";
|
||||
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
|
||||
import { TKmsServiceFactory } from "../kms/kms-service";
|
||||
@@ -43,8 +44,9 @@ import {
|
||||
} from "./identity-oidc-auth-types";
|
||||
|
||||
type TIdentityOidcAuthServiceFactoryDep = {
|
||||
identityDAL: Pick<TIdentityDALFactory, "findById">;
|
||||
identityOidcAuthDAL: TIdentityOidcAuthDALFactory;
|
||||
membershipIdentityDAL: Pick<TMembershipIdentityDALFactory, "findOne" | "updateById" | "getIdentityById">;
|
||||
membershipIdentityDAL: Pick<TMembershipIdentityDALFactory, "findOne" | "update" | "getIdentityById">;
|
||||
identityAccessTokenDAL: Pick<TIdentityAccessTokenDALFactory, "create" | "delete">;
|
||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||
@@ -55,6 +57,7 @@ type TIdentityOidcAuthServiceFactoryDep = {
|
||||
export type TIdentityOidcAuthServiceFactory = ReturnType<typeof identityOidcAuthServiceFactory>;
|
||||
|
||||
export const identityOidcAuthServiceFactory = ({
|
||||
identityDAL,
|
||||
identityOidcAuthDAL,
|
||||
membershipIdentityDAL,
|
||||
permissionService,
|
||||
@@ -69,19 +72,12 @@ export const identityOidcAuthServiceFactory = ({
|
||||
throw new NotFoundError({ message: "OIDC auth method not found for identity, did you configure OIDC auth?" });
|
||||
}
|
||||
|
||||
const identityMembershipOrg = await membershipIdentityDAL.findOne({
|
||||
actorIdentityId: identityOidcAuth.identityId,
|
||||
scope: AccessScope.Organization
|
||||
});
|
||||
if (!identityMembershipOrg) {
|
||||
throw new NotFoundError({
|
||||
message: `Identity organization membership for identity with ID '${identityOidcAuth.identityId}' not found`
|
||||
});
|
||||
}
|
||||
const identity = await identityDAL.findById(identityOidcAuth.identityId);
|
||||
if (!identity) throw new UnauthorizedError({ message: "Identity not found" });
|
||||
|
||||
const { decryptor } = await kmsService.createCipherPairWithDataKey({
|
||||
type: KmsDataKey.Organization,
|
||||
orgId: identityMembershipOrg.scopeOrgId
|
||||
orgId: identity.orgId
|
||||
});
|
||||
|
||||
let caCert = "";
|
||||
@@ -182,12 +178,9 @@ export const identityOidcAuthServiceFactory = ({
|
||||
}
|
||||
|
||||
const identityAccessToken = await identityOidcAuthDAL.transaction(async (tx) => {
|
||||
await membershipIdentityDAL.updateById(
|
||||
identityMembershipOrg.id,
|
||||
{
|
||||
lastLoginAuthMethod: IdentityAuthMethod.OIDC_AUTH,
|
||||
lastLoginTime: new Date()
|
||||
},
|
||||
await membershipIdentityDAL.update(
|
||||
{ scope: AccessScope.Organization, scopeOrgId: identity.orgId, actorIdentityId: identity.id },
|
||||
{ lastLoginAuthMethod: IdentityAuthMethod.OIDC_AUTH, lastLoginTime: new Date() },
|
||||
tx
|
||||
);
|
||||
const newToken = await identityAccessTokenDAL.create(
|
||||
@@ -226,7 +219,7 @@ export const identityOidcAuthServiceFactory = ({
|
||||
}
|
||||
);
|
||||
|
||||
return { accessToken, identityOidcAuth, identityAccessToken, identityMembershipOrg, oidcTokenData: tokenData };
|
||||
return { accessToken, identityOidcAuth, identityAccessToken, identity, oidcTokenData: tokenData };
|
||||
};
|
||||
|
||||
const attachOidcAuth = async ({
|
||||
@@ -259,6 +252,9 @@ export const identityOidcAuthServiceFactory = ({
|
||||
if (!identityMembershipOrg) {
|
||||
throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
}
|
||||
if (identityMembershipOrg.identity.identityOrgId !== actorOrgId) {
|
||||
throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" });
|
||||
}
|
||||
if (identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.OIDC_AUTH)) {
|
||||
throw new BadRequestError({
|
||||
message: "Failed to add OIDC Auth to already configured identity"
|
||||
@@ -269,13 +265,14 @@ export const identityOidcAuthServiceFactory = ({
|
||||
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Create, OrgPermissionSubjects.Identity);
|
||||
|
||||
@@ -351,6 +348,9 @@ export const identityOidcAuthServiceFactory = ({
|
||||
identityId
|
||||
});
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
if (identityMembershipOrg.identity.identityOrgId !== actorOrgId) {
|
||||
throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" });
|
||||
}
|
||||
|
||||
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.OIDC_AUTH)) {
|
||||
throw new BadRequestError({
|
||||
@@ -367,13 +367,14 @@ export const identityOidcAuthServiceFactory = ({
|
||||
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
|
||||
@@ -440,6 +441,9 @@ export const identityOidcAuthServiceFactory = ({
|
||||
identityId
|
||||
});
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
if (identityMembershipOrg.identity.identityOrgId !== actorOrgId) {
|
||||
throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" });
|
||||
}
|
||||
|
||||
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.OIDC_AUTH)) {
|
||||
throw new BadRequestError({
|
||||
@@ -447,13 +451,14 @@ export const identityOidcAuthServiceFactory = ({
|
||||
});
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity);
|
||||
|
||||
const identityOidcAuth = await identityOidcAuthDAL.findOne({ identityId });
|
||||
@@ -481,6 +486,9 @@ export const identityOidcAuthServiceFactory = ({
|
||||
if (!identityMembershipOrg) {
|
||||
throw new NotFoundError({ message: "Failed to find identity" });
|
||||
}
|
||||
if (identityMembershipOrg.identity.identityOrgId !== actorOrgId) {
|
||||
throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" });
|
||||
}
|
||||
|
||||
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.OIDC_AUTH)) {
|
||||
throw new BadRequestError({
|
||||
@@ -488,23 +496,25 @@ export const identityOidcAuthServiceFactory = ({
|
||||
});
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission(
|
||||
ActorType.IDENTITY,
|
||||
identityMembershipOrg.identity.id,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission({
|
||||
actor: ActorType.IDENTITY,
|
||||
actorId: identityMembershipOrg.identity.id,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
actorOrgId,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
|
||||
const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId);
|
||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||
|
||||
@@ -25,11 +25,12 @@ import { buildAuthMethods } from "../identity/identity-fns";
|
||||
export type TIdentityProjectDALFactory = ReturnType<typeof identityProjectDALFactory>;
|
||||
|
||||
export const identityProjectDALFactory = (db: TDbClient) => {
|
||||
const findByIdentityId = async (identityId: string, tx?: Knex) => {
|
||||
const findByIdentityId = async (identityId: string, orgId: string, tx?: Knex) => {
|
||||
try {
|
||||
const docs = await (tx || db.replicaNode())(TableName.Membership)
|
||||
.where(`${TableName.Membership}.actorIdentityId`, identityId)
|
||||
.where(`${TableName.Membership}.scope`, AccessScope.Project)
|
||||
.where(`${TableName.Membership}.scopeOrgId`, orgId)
|
||||
.whereNotNull(`${TableName.Membership}.actorIdentityId`)
|
||||
.join(TableName.Project, `${TableName.Membership}.scopeProjectId`, `${TableName.Project}.id`)
|
||||
.join(TableName.Identity, `${TableName.Membership}.actorIdentityId`, `${TableName.Identity}.id`)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
|
||||
import { AccessScope, IdentityAuthMethod } from "@app/db/schemas";
|
||||
import { AccessScope, IdentityAuthMethod, OrganizationActionScope } from "@app/db/schemas";
|
||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||
import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
|
||||
import {
|
||||
@@ -11,10 +11,17 @@ import { TPermissionServiceFactory } from "@app/ee/services/permission/permissio
|
||||
import { extractX509CertFromChain } from "@app/lib/certificates/extract-certificate";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { crypto } from "@app/lib/crypto/cryptography";
|
||||
import { BadRequestError, NotFoundError, PermissionBoundaryError, UnauthorizedError } from "@app/lib/errors";
|
||||
import {
|
||||
BadRequestError,
|
||||
ForbiddenRequestError,
|
||||
NotFoundError,
|
||||
PermissionBoundaryError,
|
||||
UnauthorizedError
|
||||
} from "@app/lib/errors";
|
||||
import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
|
||||
|
||||
import { ActorType, AuthTokenType } from "../auth/auth-type";
|
||||
import { TIdentityDALFactory } from "../identity/identity-dal";
|
||||
import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal";
|
||||
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
|
||||
import { TKmsServiceFactory } from "../kms/kms-service";
|
||||
@@ -25,12 +32,13 @@ import { TIdentityTlsCertAuthDALFactory } from "./identity-tls-cert-auth-dal";
|
||||
import { TIdentityTlsCertAuthServiceFactory } from "./identity-tls-cert-auth-types";
|
||||
|
||||
type TIdentityTlsCertAuthServiceFactoryDep = {
|
||||
identityDAL: Pick<TIdentityDALFactory, "findById">;
|
||||
identityAccessTokenDAL: Pick<TIdentityAccessTokenDALFactory, "create" | "delete">;
|
||||
identityTlsCertAuthDAL: Pick<
|
||||
TIdentityTlsCertAuthDALFactory,
|
||||
"findOne" | "transaction" | "create" | "updateById" | "delete"
|
||||
>;
|
||||
membershipIdentityDAL: Pick<TMembershipIdentityDALFactory, "findOne" | "updateById" | "getIdentityById">;
|
||||
membershipIdentityDAL: Pick<TMembershipIdentityDALFactory, "findOne" | "update" | "getIdentityById">;
|
||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
||||
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
|
||||
@@ -46,6 +54,7 @@ const parseSubjectDetails = (data: string) => {
|
||||
};
|
||||
|
||||
export const identityTlsCertAuthServiceFactory = ({
|
||||
identityDAL,
|
||||
identityAccessTokenDAL,
|
||||
identityTlsCertAuthDAL,
|
||||
membershipIdentityDAL,
|
||||
@@ -61,20 +70,12 @@ export const identityTlsCertAuthServiceFactory = ({
|
||||
});
|
||||
}
|
||||
|
||||
const identityMembershipOrg = await membershipIdentityDAL.findOne({
|
||||
actorIdentityId: identityTlsCertAuth.identityId,
|
||||
scope: AccessScope.Organization
|
||||
});
|
||||
|
||||
if (!identityMembershipOrg) {
|
||||
throw new NotFoundError({
|
||||
message: `Identity organization membership for identity with ID '${identityTlsCertAuth.identityId}' not found`
|
||||
});
|
||||
}
|
||||
const identity = await identityDAL.findById(identityTlsCertAuth.identityId);
|
||||
if (!identity) throw new UnauthorizedError({ message: "Identity not found" });
|
||||
|
||||
const { decryptor } = await kmsService.createCipherPairWithDataKey({
|
||||
type: KmsDataKey.Organization,
|
||||
orgId: identityMembershipOrg.scopeOrgId
|
||||
orgId: identity.orgId
|
||||
});
|
||||
|
||||
const caCertificate = decryptor({
|
||||
@@ -119,12 +120,9 @@ export const identityTlsCertAuthServiceFactory = ({
|
||||
|
||||
// Generate the token
|
||||
const identityAccessToken = await identityTlsCertAuthDAL.transaction(async (tx) => {
|
||||
await membershipIdentityDAL.updateById(
|
||||
identityMembershipOrg.id,
|
||||
{
|
||||
lastLoginAuthMethod: IdentityAuthMethod.TLS_CERT_AUTH,
|
||||
lastLoginTime: new Date()
|
||||
},
|
||||
await membershipIdentityDAL.update(
|
||||
{ scope: AccessScope.Organization, scopeOrgId: identity.orgId, actorIdentityId: identity.id },
|
||||
{ lastLoginAuthMethod: IdentityAuthMethod.TLS_CERT_AUTH, lastLoginTime: new Date() },
|
||||
tx
|
||||
);
|
||||
const newToken = await identityAccessTokenDAL.create(
|
||||
@@ -161,7 +159,7 @@ export const identityTlsCertAuthServiceFactory = ({
|
||||
identityTlsCertAuth,
|
||||
accessToken,
|
||||
identityAccessToken,
|
||||
identityMembershipOrg
|
||||
identity
|
||||
};
|
||||
};
|
||||
|
||||
@@ -189,6 +187,9 @@ export const identityTlsCertAuthServiceFactory = ({
|
||||
identityId
|
||||
});
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
if (identityMembershipOrg.identity.identityOrgId !== actorOrgId) {
|
||||
throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" });
|
||||
}
|
||||
|
||||
if (identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.TLS_CERT_AUTH)) {
|
||||
throw new BadRequestError({
|
||||
@@ -200,13 +201,14 @@ export const identityTlsCertAuthServiceFactory = ({
|
||||
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Create, OrgPermissionSubjects.Identity);
|
||||
|
||||
const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId);
|
||||
@@ -271,6 +273,9 @@ export const identityTlsCertAuthServiceFactory = ({
|
||||
identityId
|
||||
});
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
if (identityMembershipOrg.identity.identityOrgId !== actorOrgId) {
|
||||
throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" });
|
||||
}
|
||||
|
||||
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.TLS_CERT_AUTH)) {
|
||||
throw new NotFoundError({
|
||||
@@ -288,13 +293,14 @@ export const identityTlsCertAuthServiceFactory = ({
|
||||
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
|
||||
const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId);
|
||||
@@ -350,6 +356,9 @@ export const identityTlsCertAuthServiceFactory = ({
|
||||
identityId
|
||||
});
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
if (identityMembershipOrg.identity.identityOrgId !== actorOrgId) {
|
||||
throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" });
|
||||
}
|
||||
|
||||
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.TLS_CERT_AUTH)) {
|
||||
throw new BadRequestError({
|
||||
@@ -359,13 +368,14 @@ export const identityTlsCertAuthServiceFactory = ({
|
||||
|
||||
const identityAuth = await identityTlsCertAuthDAL.findOne({ identityId });
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity);
|
||||
const { decryptor } = await kmsService.createCipherPairWithDataKey({
|
||||
type: KmsDataKey.Organization,
|
||||
@@ -394,28 +404,32 @@ export const identityTlsCertAuthServiceFactory = ({
|
||||
identityId
|
||||
});
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
if (identityMembershipOrg.identity.identityOrgId !== actorOrgId) {
|
||||
throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" });
|
||||
}
|
||||
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.TLS_CERT_AUTH)) {
|
||||
throw new BadRequestError({
|
||||
message: "The identity does not have TLS Certificate auth"
|
||||
});
|
||||
}
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
|
||||
const { permission: rolePermission, memberships } = await permissionService.getOrgPermission(
|
||||
ActorType.IDENTITY,
|
||||
identityMembershipOrg.identity.id,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
const { permission: rolePermission, memberships } = await permissionService.getOrgPermission({
|
||||
actor: ActorType.IDENTITY,
|
||||
actorId: identityMembershipOrg.identity.id,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
|
||||
actorOrgId,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
const shouldUseNewPrivilegeSystem = Boolean(memberships?.[0]?.shouldUseNewPrivilegeSystem);
|
||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||
shouldUseNewPrivilegeSystem,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { TIdentityAccessTokens, TIdentityTlsCertAuths, TMemberships } from "@app/db/schemas";
|
||||
import { TIdentities, TIdentityAccessTokens, TIdentityTlsCertAuths } from "@app/db/schemas";
|
||||
import { TProjectPermission } from "@app/lib/types";
|
||||
|
||||
export type TLoginTlsCertAuthDTO = {
|
||||
@@ -40,7 +40,7 @@ export type TIdentityTlsCertAuthServiceFactory = {
|
||||
identityTlsCertAuth: TIdentityTlsCertAuths;
|
||||
accessToken: string;
|
||||
identityAccessToken: TIdentityAccessTokens;
|
||||
identityMembershipOrg: TMemberships;
|
||||
identity: TIdentities;
|
||||
}>;
|
||||
attachTlsCertAuth: (dto: TAttachTlsCertAuthDTO) => Promise<TIdentityTlsCertAuths>;
|
||||
updateTlsCertAuth: (dto: TUpdateTlsCertAuthDTO) => Promise<TIdentityTlsCertAuths>;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
|
||||
import { AccessScope, IdentityAuthMethod, TableName } from "@app/db/schemas";
|
||||
import { AccessScope, IdentityAuthMethod, OrganizationActionScope, TableName } from "@app/db/schemas";
|
||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||
import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
|
||||
import {
|
||||
@@ -10,10 +10,17 @@ import {
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { crypto } from "@app/lib/crypto";
|
||||
import { BadRequestError, NotFoundError, PermissionBoundaryError } from "@app/lib/errors";
|
||||
import {
|
||||
BadRequestError,
|
||||
ForbiddenRequestError,
|
||||
NotFoundError,
|
||||
PermissionBoundaryError,
|
||||
UnauthorizedError
|
||||
} from "@app/lib/errors";
|
||||
import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
|
||||
|
||||
import { ActorType, AuthTokenType } from "../auth/auth-type";
|
||||
import { TIdentityDALFactory } from "../identity/identity-dal";
|
||||
import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal";
|
||||
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
|
||||
import { TMembershipIdentityDALFactory } from "../membership-identity/membership-identity-dal";
|
||||
@@ -32,11 +39,12 @@ import {
|
||||
} from "./identity-token-auth-types";
|
||||
|
||||
type TIdentityTokenAuthServiceFactoryDep = {
|
||||
identityDAL: Pick<TIdentityDALFactory, "findById">;
|
||||
identityTokenAuthDAL: Pick<
|
||||
TIdentityTokenAuthDALFactory,
|
||||
"transaction" | "create" | "findOne" | "updateById" | "delete"
|
||||
>;
|
||||
membershipIdentityDAL: Pick<TMembershipIdentityDALFactory, "findOne" | "updateById" | "getIdentityById">;
|
||||
membershipIdentityDAL: Pick<TMembershipIdentityDALFactory, "findOne" | "update" | "getIdentityById">;
|
||||
identityAccessTokenDAL: Pick<
|
||||
TIdentityAccessTokenDALFactory,
|
||||
"create" | "find" | "update" | "findById" | "findOne" | "updateById" | "delete"
|
||||
@@ -49,8 +57,8 @@ type TIdentityTokenAuthServiceFactoryDep = {
|
||||
export type TIdentityTokenAuthServiceFactory = ReturnType<typeof identityTokenAuthServiceFactory>;
|
||||
|
||||
export const identityTokenAuthServiceFactory = ({
|
||||
identityDAL,
|
||||
identityTokenAuthDAL,
|
||||
// identityDAL,
|
||||
membershipIdentityDAL,
|
||||
identityAccessTokenDAL,
|
||||
permissionService,
|
||||
@@ -79,6 +87,9 @@ export const identityTokenAuthServiceFactory = ({
|
||||
identityId
|
||||
});
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
if (identityMembershipOrg.identity.identityOrgId !== actorOrgId) {
|
||||
throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" });
|
||||
}
|
||||
|
||||
if (identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.TOKEN_AUTH)) {
|
||||
throw new BadRequestError({
|
||||
@@ -90,13 +101,14 @@ export const identityTokenAuthServiceFactory = ({
|
||||
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Create, OrgPermissionSubjects.Identity);
|
||||
|
||||
const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId);
|
||||
@@ -155,6 +167,9 @@ export const identityTokenAuthServiceFactory = ({
|
||||
identityId
|
||||
});
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
if (identityMembershipOrg.identity.identityOrgId !== actorOrgId) {
|
||||
throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" });
|
||||
}
|
||||
|
||||
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.TOKEN_AUTH)) {
|
||||
throw new BadRequestError({
|
||||
@@ -172,13 +187,14 @@ export const identityTokenAuthServiceFactory = ({
|
||||
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
|
||||
const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId);
|
||||
@@ -223,6 +239,9 @@ export const identityTokenAuthServiceFactory = ({
|
||||
identityId
|
||||
});
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
if (identityMembershipOrg.identity.identityOrgId !== actorOrgId) {
|
||||
throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" });
|
||||
}
|
||||
|
||||
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.TOKEN_AUTH)) {
|
||||
throw new BadRequestError({
|
||||
@@ -232,13 +251,14 @@ export const identityTokenAuthServiceFactory = ({
|
||||
|
||||
const identityTokenAuth = await identityTokenAuthDAL.findOne({ identityId });
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity);
|
||||
|
||||
return { ...identityTokenAuth, orgId: identityMembershipOrg.scopeOrgId };
|
||||
@@ -262,28 +282,33 @@ export const identityTokenAuthServiceFactory = ({
|
||||
identityId
|
||||
});
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
if (identityMembershipOrg.identity.identityOrgId !== actorOrgId) {
|
||||
throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" });
|
||||
}
|
||||
|
||||
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.TOKEN_AUTH)) {
|
||||
throw new BadRequestError({
|
||||
message: "The identity does not have Token Auth"
|
||||
});
|
||||
}
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission(
|
||||
ActorType.IDENTITY,
|
||||
identityMembershipOrg.identity.id,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission({
|
||||
actor: ActorType.IDENTITY,
|
||||
actorId: identityMembershipOrg.identity.id,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
actorOrgId,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
|
||||
const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId);
|
||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||
@@ -341,22 +366,26 @@ export const identityTokenAuthServiceFactory = ({
|
||||
message: "The identity does not have Token Auth"
|
||||
});
|
||||
}
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission(
|
||||
ActorType.IDENTITY,
|
||||
identityMembershipOrg.identity.id,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission({
|
||||
actor: ActorType.IDENTITY,
|
||||
actorId: identityMembershipOrg.identity.id,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
actorOrgId,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
|
||||
const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId);
|
||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||
@@ -379,13 +408,13 @@ export const identityTokenAuthServiceFactory = ({
|
||||
|
||||
const identityTokenAuth = await identityTokenAuthDAL.findOne({ identityId });
|
||||
|
||||
const identity = await identityDAL.findById(identityTokenAuth.identityId);
|
||||
if (!identity) throw new UnauthorizedError({ message: "Identity not found" });
|
||||
|
||||
const identityAccessToken = await identityTokenAuthDAL.transaction(async (tx) => {
|
||||
await membershipIdentityDAL.updateById(
|
||||
identityMembershipOrg.id,
|
||||
{
|
||||
lastLoginAuthMethod: IdentityAuthMethod.TOKEN_AUTH,
|
||||
lastLoginTime: new Date()
|
||||
},
|
||||
await membershipIdentityDAL.update(
|
||||
{ scope: AccessScope.Organization, scopeOrgId: identity.orgId, actorIdentityId: identity.id },
|
||||
{ lastLoginAuthMethod: IdentityAuthMethod.TOKEN_AUTH, lastLoginTime: new Date() },
|
||||
tx
|
||||
);
|
||||
const newToken = await identityAccessTokenDAL.create(
|
||||
@@ -420,7 +449,7 @@ export const identityTokenAuthServiceFactory = ({
|
||||
}
|
||||
);
|
||||
|
||||
return { accessToken, identityTokenAuth, identityAccessToken, identityMembershipOrg };
|
||||
return { accessToken, identityTokenAuth, identityAccessToken, identity };
|
||||
};
|
||||
|
||||
const getTokenAuthTokens = async ({
|
||||
@@ -449,13 +478,14 @@ export const identityTokenAuthServiceFactory = ({
|
||||
message: "The identity does not have Token Auth"
|
||||
});
|
||||
}
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity);
|
||||
|
||||
const tokens = await identityAccessTokenDAL.find(
|
||||
@@ -501,22 +531,24 @@ export const identityTokenAuthServiceFactory = ({
|
||||
message: "The identity does not have Token Auth"
|
||||
});
|
||||
}
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission(
|
||||
ActorType.IDENTITY,
|
||||
identityMembershipOrg.identity.id,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission({
|
||||
actor: ActorType.IDENTITY,
|
||||
actorId: identityMembershipOrg.identity.id,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
actorOrgId,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId);
|
||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||
shouldUseNewPrivilegeSystem,
|
||||
@@ -580,13 +612,14 @@ export const identityTokenAuthServiceFactory = ({
|
||||
throw new NotFoundError({ message: `Failed to find identity with ID ${identityAccessToken.identityId}` });
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
identityOrgMembership.scopeOrgId,
|
||||
orgId: identityOrgMembership.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
|
||||
const [revokedToken] = await identityAccessTokenDAL.update(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
|
||||
import { AccessScope, IdentityAuthMethod } from "@app/db/schemas";
|
||||
import { AccessScope, IdentityAuthMethod, OrganizationActionScope } from "@app/db/schemas";
|
||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||
import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
|
||||
import {
|
||||
@@ -13,6 +13,7 @@ import { getConfig } from "@app/lib/config/env";
|
||||
import { crypto } from "@app/lib/crypto/cryptography";
|
||||
import {
|
||||
BadRequestError,
|
||||
ForbiddenRequestError,
|
||||
NotFoundError,
|
||||
PermissionBoundaryError,
|
||||
RateLimitError,
|
||||
@@ -22,6 +23,7 @@ import { checkIPAgainstBlocklist, extractIPDetails, isValidIpOrCidr, TIp } from
|
||||
import { logger } from "@app/lib/logger";
|
||||
|
||||
import { ActorType, AuthTokenType } from "../auth/auth-type";
|
||||
import { TIdentityDALFactory } from "../identity/identity-dal";
|
||||
import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal";
|
||||
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
|
||||
import { TMembershipIdentityDALFactory } from "../membership-identity/membership-identity-dal";
|
||||
@@ -42,6 +44,7 @@ import {
|
||||
} from "./identity-ua-types";
|
||||
|
||||
type TIdentityUaServiceFactoryDep = {
|
||||
identityDAL: Pick<TIdentityDALFactory, "findById">;
|
||||
identityUaDAL: TIdentityUaDALFactory;
|
||||
identityUaClientSecretDAL: TIdentityUaClientSecretDALFactory;
|
||||
identityAccessTokenDAL: TIdentityAccessTokenDALFactory;
|
||||
@@ -70,7 +73,8 @@ export const identityUaServiceFactory = ({
|
||||
permissionService,
|
||||
licenseService,
|
||||
orgDAL,
|
||||
keyStore
|
||||
keyStore,
|
||||
identityDAL
|
||||
}: TIdentityUaServiceFactoryDep) => {
|
||||
const login = async (clientId: string, clientSecret: string, ip: string) => {
|
||||
const identityUa = await identityUaDAL.findOne({ clientId });
|
||||
@@ -100,16 +104,6 @@ export const identityUaServiceFactory = ({
|
||||
});
|
||||
}
|
||||
|
||||
const identityMembershipOrg = await membershipIdentityDAL.findOne({
|
||||
actorIdentityId: identityUa.identityId,
|
||||
scope: AccessScope.Organization
|
||||
});
|
||||
if (!identityMembershipOrg) {
|
||||
throw new UnauthorizedError({
|
||||
message: "Invalid credentials"
|
||||
});
|
||||
}
|
||||
|
||||
const clientSecretPrefix = clientSecret.slice(0, 4);
|
||||
const clientSecretInfo = await identityUaClientSecretDAL.find({
|
||||
identityUAId: identityUa.id,
|
||||
@@ -227,10 +221,11 @@ export const identityUaServiceFactory = ({
|
||||
accessTokenMaxTTL: 1000000000
|
||||
};
|
||||
|
||||
const identity = await identityDAL.findById(identityUa.identityId);
|
||||
const identityAccessToken = await identityUaDAL.transaction(async (tx) => {
|
||||
const uaClientSecretDoc = await identityUaClientSecretDAL.incrementUsage(validClientSecretInfo!.id, tx);
|
||||
await membershipIdentityDAL.updateById(
|
||||
identityMembershipOrg.id,
|
||||
await membershipIdentityDAL.update(
|
||||
{ scope: AccessScope.Organization, scopeOrgId: identity.orgId, actorIdentityId: identity.id },
|
||||
{
|
||||
lastLoginAuthMethod: IdentityAuthMethod.UNIVERSAL_AUTH,
|
||||
lastLoginTime: new Date()
|
||||
@@ -276,7 +271,7 @@ export const identityUaServiceFactory = ({
|
||||
identityUa,
|
||||
validClientSecretInfo,
|
||||
identityAccessToken,
|
||||
identityMembershipOrg,
|
||||
identity,
|
||||
...accessTokenTTLParams
|
||||
};
|
||||
};
|
||||
@@ -315,18 +310,23 @@ export const identityUaServiceFactory = ({
|
||||
message: "Failed to add universal auth to already configured identity"
|
||||
});
|
||||
}
|
||||
if (identityMembershipOrg.identity.identityOrgId !== actorOrgId) {
|
||||
throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" });
|
||||
}
|
||||
|
||||
if (accessTokenMaxTTL > 0 && accessTokenTTL > accessTokenMaxTTL) {
|
||||
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Create, OrgPermissionSubjects.Identity);
|
||||
|
||||
const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId);
|
||||
@@ -423,6 +423,10 @@ export const identityUaServiceFactory = ({
|
||||
});
|
||||
}
|
||||
|
||||
if (identityMembershipOrg.identity.identityOrgId !== actorOrgId) {
|
||||
throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" });
|
||||
}
|
||||
|
||||
if (
|
||||
(accessTokenMaxTTL || uaIdentityAuth.accessTokenMaxTTL) > 0 &&
|
||||
(accessTokenTTL || uaIdentityAuth.accessTokenMaxTTL) > (accessTokenMaxTTL || uaIdentityAuth.accessTokenMaxTTL)
|
||||
@@ -430,13 +434,14 @@ export const identityUaServiceFactory = ({
|
||||
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
|
||||
const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId);
|
||||
@@ -512,14 +517,18 @@ export const identityUaServiceFactory = ({
|
||||
message: "The identity does not have universal auth"
|
||||
});
|
||||
}
|
||||
if (identityMembershipOrg.identity.identityOrgId !== actorOrgId) {
|
||||
throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" });
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity);
|
||||
return { ...uaIdentityAuth, orgId: identityMembershipOrg.scopeOrgId };
|
||||
};
|
||||
@@ -545,22 +554,27 @@ export const identityUaServiceFactory = ({
|
||||
message: "The identity does not have universal auth"
|
||||
});
|
||||
}
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
if (identityMembershipOrg.identity.identityOrgId !== actorOrgId) {
|
||||
throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" });
|
||||
}
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission(
|
||||
ActorType.IDENTITY,
|
||||
identityMembershipOrg.identity.id,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission({
|
||||
actor: ActorType.IDENTITY,
|
||||
actorId: identityMembershipOrg.identity.id,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
actorOrgId,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId);
|
||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||
shouldUseNewPrivilegeSystem,
|
||||
@@ -611,23 +625,28 @@ export const identityUaServiceFactory = ({
|
||||
message: "The identity does not have universal auth"
|
||||
});
|
||||
}
|
||||
if (identityMembershipOrg.identity.identityOrgId !== actorOrgId) {
|
||||
throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" });
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Create, OrgPermissionSubjects.Identity);
|
||||
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission(
|
||||
ActorType.IDENTITY,
|
||||
identityMembershipOrg.identity.id,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission({
|
||||
actor: ActorType.IDENTITY,
|
||||
actorId: identityMembershipOrg.identity.id,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
actorOrgId,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId);
|
||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||
shouldUseNewPrivilegeSystem,
|
||||
@@ -692,23 +711,28 @@ export const identityUaServiceFactory = ({
|
||||
message: "The identity does not have universal auth"
|
||||
});
|
||||
}
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
if (identityMembershipOrg.identity.identityOrgId !== actorOrgId) {
|
||||
throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" });
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity);
|
||||
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission(
|
||||
ActorType.IDENTITY,
|
||||
identityMembershipOrg.identity.id,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission({
|
||||
actor: ActorType.IDENTITY,
|
||||
actorId: identityMembershipOrg.identity.id,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
|
||||
actorOrgId,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId);
|
||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||
shouldUseNewPrivilegeSystem,
|
||||
@@ -761,6 +785,9 @@ export const identityUaServiceFactory = ({
|
||||
message: "The identity does not have universal auth"
|
||||
});
|
||||
}
|
||||
if (identityMembershipOrg.identity.identityOrgId !== actorOrgId) {
|
||||
throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" });
|
||||
}
|
||||
|
||||
const identityUa = await identityUaDAL.findOne({ identityId });
|
||||
if (!identityUa) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
@@ -768,22 +795,24 @@ export const identityUaServiceFactory = ({
|
||||
const clientSecret = await identityUaClientSecretDAL.findOne({ id: clientSecretId, identityUAId: identityUa.id });
|
||||
if (!clientSecret) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity);
|
||||
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission(
|
||||
ActorType.IDENTITY,
|
||||
identityMembershipOrg.identity.id,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission({
|
||||
actor: ActorType.IDENTITY,
|
||||
actorId: identityMembershipOrg.identity.id,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
actorOrgId,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId);
|
||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||
shouldUseNewPrivilegeSystem,
|
||||
@@ -828,6 +857,9 @@ export const identityUaServiceFactory = ({
|
||||
message: "The identity does not have universal auth"
|
||||
});
|
||||
}
|
||||
if (identityMembershipOrg.identity.identityOrgId !== actorOrgId) {
|
||||
throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" });
|
||||
}
|
||||
|
||||
const identityUa = await identityUaDAL.findOne({ identityId });
|
||||
if (!identityUa) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
@@ -835,22 +867,24 @@ export const identityUaServiceFactory = ({
|
||||
const clientSecret = await identityUaClientSecretDAL.findOne({ id: clientSecretId, identityUAId: identityUa.id });
|
||||
if (!clientSecret) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Delete, OrgPermissionSubjects.Identity);
|
||||
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission(
|
||||
ActorType.IDENTITY,
|
||||
identityMembershipOrg.identity.id,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission({
|
||||
actor: ActorType.IDENTITY,
|
||||
actorId: identityMembershipOrg.identity.id,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
actorOrgId,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
|
||||
const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId);
|
||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||
@@ -900,14 +934,18 @@ export const identityUaServiceFactory = ({
|
||||
message: "The identity does not have universal auth"
|
||||
});
|
||||
}
|
||||
if (identityMembershipOrg.identity.identityOrgId !== actorOrgId) {
|
||||
throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" });
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.scopeOrgId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
|
||||
const deleted = await keyStore.deleteItems({
|
||||
|
||||
@@ -163,7 +163,8 @@ export const identityOrgDALFactory = (db: TDbClient) => {
|
||||
.select(
|
||||
selectAllTableCols(TableName.Membership),
|
||||
db.ref("name").withSchema(TableName.Identity).as("identityName"),
|
||||
db.ref("hasDeleteProtection").withSchema(TableName.Identity)
|
||||
db.ref("hasDeleteProtection").withSchema(TableName.Identity),
|
||||
db.ref("orgId").withSchema(TableName.Identity)
|
||||
)
|
||||
.where(filter)
|
||||
.as("paginatedIdentity");
|
||||
@@ -257,6 +258,7 @@ export const identityOrgDALFactory = (db: TDbClient) => {
|
||||
db.ref("customRoleId").withSchema(TableName.MembershipRole).as("roleId"),
|
||||
db.ref("scopeOrgId").withSchema("paginatedIdentity").as("orgId"),
|
||||
db.ref("lastLoginAuthMethod").withSchema("paginatedIdentity"),
|
||||
db.ref("orgId").withSchema("paginatedIdentity").as("identityOrgId"),
|
||||
db.ref("lastLoginTime").withSchema("paginatedIdentity"),
|
||||
db.ref("createdAt").withSchema("paginatedIdentity"),
|
||||
db.ref("updatedAt").withSchema("paginatedIdentity"),
|
||||
@@ -309,6 +311,7 @@ export const identityOrgDALFactory = (db: TDbClient) => {
|
||||
roleId,
|
||||
id,
|
||||
orgId,
|
||||
identityOrgId,
|
||||
uaId,
|
||||
alicloudId,
|
||||
awsId,
|
||||
@@ -348,6 +351,7 @@ export const identityOrgDALFactory = (db: TDbClient) => {
|
||||
id: identityId as string,
|
||||
name: identityName,
|
||||
hasDeleteProtection,
|
||||
orgId: identityOrgId,
|
||||
authMethods: buildAuthMethods({
|
||||
uaId,
|
||||
alicloudId,
|
||||
@@ -515,6 +519,7 @@ export const identityOrgDALFactory = (db: TDbClient) => {
|
||||
db.ref("actorIdentityId").withSchema(TableName.Membership).as("identityId"),
|
||||
db.ref("name").withSchema(TableName.Identity).as("identityName"),
|
||||
db.ref("hasDeleteProtection").withSchema(TableName.Identity),
|
||||
db.ref("orgId").withSchema(TableName.Identity).as("identityOrgId"),
|
||||
|
||||
db.ref("id").as("uaId").withSchema(TableName.IdentityUniversalAuth),
|
||||
db.ref("id").as("gcpId").withSchema(TableName.IdentityGcpAuth),
|
||||
@@ -566,6 +571,7 @@ export const identityOrgDALFactory = (db: TDbClient) => {
|
||||
crPermission,
|
||||
crName,
|
||||
identityId,
|
||||
identityOrgId,
|
||||
identityName,
|
||||
hasDeleteProtection,
|
||||
role,
|
||||
@@ -611,6 +617,7 @@ export const identityOrgDALFactory = (db: TDbClient) => {
|
||||
id: identityId as string,
|
||||
name: identityName,
|
||||
hasDeleteProtection,
|
||||
orgId: identityOrgId,
|
||||
authMethods: buildAuthMethods({
|
||||
uaId,
|
||||
alicloudId,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
|
||||
import { AccessScope, OrgMembershipRole, TableName, TRoles } from "@app/db/schemas";
|
||||
import { AccessScope, OrganizationActionScope, OrgMembershipRole, TableName, TRoles } from "@app/db/schemas";
|
||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||
import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
|
||||
import {
|
||||
@@ -12,6 +12,7 @@ import { TKeyStoreFactory } from "@app/keystore/keystore";
|
||||
import { BadRequestError, NotFoundError, PermissionBoundaryError } from "@app/lib/errors";
|
||||
import { TIdentityProjectDALFactory } from "@app/services/identity-project/identity-project-dal";
|
||||
|
||||
import { TAdditionalPrivilegeDALFactory } from "../additional-privilege/additional-privilege-dal";
|
||||
import { TMembershipRoleDALFactory } from "../membership/membership-role-dal";
|
||||
import { TMembershipIdentityDALFactory } from "../membership-identity/membership-identity-dal";
|
||||
import { TOrgDALFactory } from "../org/org-dal";
|
||||
@@ -40,6 +41,7 @@ type TIdentityServiceFactoryDep = {
|
||||
licenseService: Pick<TLicenseServiceFactory, "getPlan" | "updateSubscriptionOrgMemberCount">;
|
||||
keyStore: Pick<TKeyStoreFactory, "getKeysByPattern" | "getItem">;
|
||||
orgDAL: Pick<TOrgDALFactory, "findById">;
|
||||
additionalPrivilegeDAL: Pick<TAdditionalPrivilegeDALFactory, "delete">;
|
||||
};
|
||||
|
||||
export type TIdentityServiceFactory = ReturnType<typeof identityServiceFactory>;
|
||||
@@ -54,7 +56,8 @@ export const identityServiceFactory = ({
|
||||
keyStore,
|
||||
orgDAL,
|
||||
membershipIdentityDAL,
|
||||
membershipRoleDAL
|
||||
membershipRoleDAL,
|
||||
additionalPrivilegeDAL
|
||||
}: TIdentityServiceFactoryDep) => {
|
||||
const createIdentity = async ({
|
||||
name,
|
||||
@@ -67,7 +70,14 @@ export const identityServiceFactory = ({
|
||||
actorOrgId,
|
||||
metadata
|
||||
}: TCreateIdentityDTO) => {
|
||||
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(OrgPermissionIdentityActions.Create, OrgPermissionSubjects.Identity);
|
||||
|
||||
const [rolePermissionDetails] = await permissionService.getOrgPermissionByRoles([role], orgId);
|
||||
@@ -104,7 +114,7 @@ export const identityServiceFactory = ({
|
||||
}
|
||||
|
||||
const identity = await identityDAL.transaction(async (tx) => {
|
||||
const newIdentity = await identityDAL.create({ name, hasDeleteProtection }, tx);
|
||||
const newIdentity = await identityDAL.create({ name, hasDeleteProtection, orgId }, tx);
|
||||
const membership = await membershipIdentityDAL.create(
|
||||
{
|
||||
scope: AccessScope.Organization,
|
||||
@@ -172,13 +182,14 @@ export const identityServiceFactory = ({
|
||||
});
|
||||
if (!identityOrgMembership) throw new NotFoundError({ message: `Failed to find identity with id ${id}` });
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
identityOrgMembership.scopeOrgId,
|
||||
orgId: identityOrgMembership.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
|
||||
let customRole: TRoles | undefined;
|
||||
@@ -208,11 +219,12 @@ export const identityServiceFactory = ({
|
||||
if (isCustomRole) customRole = rolePermissionDetails?.role;
|
||||
}
|
||||
|
||||
const identityDetails = await identityDAL.findById(id);
|
||||
const identity = await identityDAL.transaction(async (tx) => {
|
||||
const newIdentity =
|
||||
name || hasDeleteProtection
|
||||
identityDetails.orgId === actorOrgId && (name || hasDeleteProtection)
|
||||
? await identityDAL.updateById(id, { name, hasDeleteProtection }, tx)
|
||||
: await identityDAL.findById(id, tx);
|
||||
: identityDetails;
|
||||
|
||||
if (role) {
|
||||
await membershipRoleDAL.delete({ membershipId: identityOrgMembership.id }, tx);
|
||||
@@ -264,16 +276,16 @@ export const identityServiceFactory = ({
|
||||
const identity = doc[0];
|
||||
if (!identity) throw new NotFoundError({ message: `Failed to find identity with id ${id}` });
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
identity.orgId,
|
||||
orgId: identity.orgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity);
|
||||
|
||||
// TODO(namespace): check this in identity service
|
||||
const activeLockouts = await keyStore.getKeysByPattern(`lockout:identity:${id}:*`);
|
||||
|
||||
const activeLockoutAuthMethods = new Set<string>();
|
||||
@@ -314,23 +326,56 @@ export const identityServiceFactory = ({
|
||||
});
|
||||
if (!identityOrgMembership) throw new NotFoundError({ message: `Failed to find identity with id ${id}` });
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
identityOrgMembership.scopeOrgId,
|
||||
orgId: identityOrgMembership.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Delete, OrgPermissionSubjects.Identity);
|
||||
|
||||
if (identityOrgMembership.identity.hasDeleteProtection)
|
||||
throw new BadRequestError({ message: "Identity has delete protection" });
|
||||
|
||||
if (identityOrgMembership.identity.identityOrgId === actorOrgId) {
|
||||
const deletedIdentity = await identityDAL.deleteById(id);
|
||||
|
||||
await licenseService.updateSubscriptionOrgMemberCount(identityOrgMembership.scopeOrgId);
|
||||
return { ...deletedIdentity, orgId: identityOrgMembership.scopeOrgId };
|
||||
}
|
||||
|
||||
await membershipIdentityDAL.transaction(async (tx) => {
|
||||
await identityMetadataDAL.delete(
|
||||
{
|
||||
identityId: id,
|
||||
orgId: actorOrgId
|
||||
},
|
||||
tx
|
||||
);
|
||||
const identityProjectMembership = await membershipIdentityDAL.find(
|
||||
{
|
||||
actorIdentityId: id,
|
||||
scope: AccessScope.Project,
|
||||
scopeOrgId: actorOrgId
|
||||
},
|
||||
{ tx }
|
||||
);
|
||||
await additionalPrivilegeDAL.delete(
|
||||
{
|
||||
actorIdentityId: id,
|
||||
$in: {
|
||||
projectId: identityProjectMembership.map((el) => el.scopeProjectId)
|
||||
}
|
||||
},
|
||||
tx
|
||||
);
|
||||
const doc = await membershipIdentityDAL.delete({ actorIdentityId: id, scopeOrgId: actorOrgId }, tx);
|
||||
return doc;
|
||||
});
|
||||
|
||||
const deletedIdentity = await identityDAL.findById(id);
|
||||
return { ...deletedIdentity, orgId: identityOrgMembership.scopeOrgId };
|
||||
};
|
||||
|
||||
@@ -346,7 +391,14 @@ export const identityServiceFactory = ({
|
||||
orderDirection,
|
||||
search
|
||||
}: TListOrgIdentitiesByOrgIdDTO) => {
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: actorOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity);
|
||||
|
||||
const identityMemberships = await identityOrgMembershipDAL.find({
|
||||
@@ -379,7 +431,14 @@ export const identityServiceFactory = ({
|
||||
orderDirection,
|
||||
searchFilter = {}
|
||||
}: TSearchOrgIdentitiesByOrgIdDTO) => {
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: actorOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity);
|
||||
|
||||
const { totalCount, docs } = await identityOrgMembershipDAL.searchIdentities({
|
||||
@@ -408,16 +467,17 @@ export const identityServiceFactory = ({
|
||||
});
|
||||
if (!identityOrgMembership) throw new NotFoundError({ message: `Failed to find identity with id ${identityId}` });
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
identityOrgMembership.scopeOrgId,
|
||||
orgId: identityOrgMembership.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity);
|
||||
|
||||
const identityMemberships = await identityProjectDAL.findByIdentityId(identityId);
|
||||
const identityMemberships = await identityProjectDAL.findByIdentityId(identityId, actorOrgId);
|
||||
return identityMemberships;
|
||||
};
|
||||
|
||||
|
||||
@@ -112,7 +112,7 @@ export const integrationAuthServiceFactory = ({
|
||||
};
|
||||
|
||||
const listOrgIntegrationAuth = async ({ actorId, actor, actorOrgId, actorAuthMethod }: TGenericPermission) => {
|
||||
const authorizations = await integrationAuthDAL.getByOrg(actorOrgId as string);
|
||||
const authorizations = await integrationAuthDAL.getByOrg(actorOrgId);
|
||||
|
||||
const filteredAuthorizations = await Promise.all(
|
||||
authorizations.map(async (auth) => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
|
||||
import { AccessScope, OrgMembershipRole } from "@app/db/schemas";
|
||||
import { AccessScope, OrganizationActionScope, OrgMembershipRole } from "@app/db/schemas";
|
||||
import { OrgPermissionGroupActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
|
||||
import {
|
||||
constructPermissionErrorMessage,
|
||||
@@ -45,13 +45,14 @@ export const newOrgMembershipGroupFactory = ({
|
||||
};
|
||||
|
||||
const onUpdateMembershipGroupGuard: TMembershipGroupScopeFactory["onUpdateMembershipGroupGuard"] = async (dto) => {
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
dto.permission.type,
|
||||
dto.permission.id,
|
||||
dto.permission.orgId,
|
||||
dto.permission.authMethod,
|
||||
dto.permission.orgId
|
||||
);
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
actor: dto.permission.type,
|
||||
actorId: dto.permission.id,
|
||||
orgId: dto.permission.orgId,
|
||||
actorAuthMethod: dto.permission.authMethod,
|
||||
actorOrgId: dto.permission.orgId,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionGroupActions.Edit, OrgPermissionSubjects.Groups);
|
||||
const permissionRoles = await permissionService.getOrgPermissionByRoles(
|
||||
dto.data.roles.map((el) => el.role),
|
||||
@@ -89,26 +90,28 @@ export const newOrgMembershipGroupFactory = ({
|
||||
};
|
||||
|
||||
const onListMembershipGroupGuard: TMembershipGroupScopeFactory["onListMembershipGroupGuard"] = async (dto) => {
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
dto.permission.type,
|
||||
dto.permission.id,
|
||||
dto.permission.orgId,
|
||||
dto.permission.authMethod,
|
||||
dto.permission.orgId
|
||||
);
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
actor: dto.permission.type,
|
||||
actorId: dto.permission.id,
|
||||
orgId: dto.permission.orgId,
|
||||
actorAuthMethod: dto.permission.authMethod,
|
||||
actorOrgId: dto.permission.orgId,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionGroupActions.Read, OrgPermissionSubjects.Groups);
|
||||
};
|
||||
|
||||
const onGetMembershipGroupByGroupIdGuard: TMembershipGroupScopeFactory["onGetMembershipGroupByGroupIdGuard"] = async (
|
||||
dto
|
||||
) => {
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
dto.permission.type,
|
||||
dto.permission.id,
|
||||
dto.permission.orgId,
|
||||
dto.permission.authMethod,
|
||||
dto.permission.orgId
|
||||
);
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
actor: dto.permission.type,
|
||||
actorId: dto.permission.id,
|
||||
orgId: dto.permission.orgId,
|
||||
actorAuthMethod: dto.permission.authMethod,
|
||||
actorOrgId: dto.permission.orgId,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionGroupActions.Read, OrgPermissionSubjects.Groups);
|
||||
};
|
||||
|
||||
|
||||
@@ -91,6 +91,7 @@ export const membershipIdentityDALFactory = (db: TDbClient) => {
|
||||
.select(
|
||||
db.ref("name").withSchema(TableName.Identity).as("identityName"),
|
||||
db.ref("id").withSchema(TableName.Identity).as("identityId"),
|
||||
db.ref("orgId").withSchema(TableName.Identity).as("identityOrgId"),
|
||||
db.ref("hasDeleteProtection").withSchema(TableName.Identity).as("identityHasDeleteProtection"),
|
||||
|
||||
db.ref("slug").withSchema(TableName.Role).as("roleSlug"),
|
||||
@@ -132,6 +133,7 @@ export const membershipIdentityDALFactory = (db: TDbClient) => {
|
||||
parentMapper: (el) => {
|
||||
const {
|
||||
identityId: actorIdentityId,
|
||||
identityOrgId,
|
||||
identityHasDeleteProtection,
|
||||
identityName,
|
||||
uaId,
|
||||
@@ -153,6 +155,7 @@ export const membershipIdentityDALFactory = (db: TDbClient) => {
|
||||
name: identityName,
|
||||
id: actorIdentityId,
|
||||
hasDeleteProtection: identityHasDeleteProtection,
|
||||
identityOrgId,
|
||||
authMethods: buildAuthMethods({
|
||||
uaId,
|
||||
awsId,
|
||||
@@ -353,5 +356,34 @@ export const membershipIdentityDALFactory = (db: TDbClient) => {
|
||||
}
|
||||
};
|
||||
|
||||
return { ...orm, findIdentities, getIdentityById };
|
||||
// this right now only support sub organization
|
||||
const listAvailableIdentities = async (orgId: string, rootOrgId: string) => {
|
||||
try {
|
||||
const usersConnectedToOrg = db
|
||||
.replicaNode()(TableName.Membership)
|
||||
.whereNotNull(`${TableName.Membership}.actorIdentityId`)
|
||||
.where(`${TableName.Membership}.scope`, AccessScope.Organization)
|
||||
.where(`${TableName.Membership}.scopeOrgId`, orgId)
|
||||
.select("actorIdentityId");
|
||||
|
||||
const docs = await db
|
||||
.replicaNode()(TableName.Membership)
|
||||
.join(TableName.Identity, `${TableName.Identity}.id`, `${TableName.Membership}.actorIdentityId`)
|
||||
.where(`${TableName.Membership}.scope`, AccessScope.Organization)
|
||||
.whereNotNull(`${TableName.Membership}.actorIdentityId`)
|
||||
.where(`${TableName.Membership}.scopeOrgId`, rootOrgId)
|
||||
.whereNotIn(`${TableName.Membership}.actorIdentityId`, usersConnectedToOrg)
|
||||
.select(
|
||||
db.ref("id").withSchema(TableName.Identity),
|
||||
db.ref("name").withSchema(TableName.Identity),
|
||||
db.ref("hasDeleteProtection").withSchema(TableName.Identity)
|
||||
);
|
||||
|
||||
return docs;
|
||||
} catch (error) {
|
||||
throw new DatabaseError({ error, name: "ListAvailableIdentities" });
|
||||
}
|
||||
};
|
||||
|
||||
return { ...orm, findIdentities, getIdentityById, listAvailableIdentities };
|
||||
};
|
||||
|
||||
@@ -6,6 +6,7 @@ import { ms } from "@app/lib/ms";
|
||||
import { SearchResourceOperators } from "@app/lib/search-resource/search";
|
||||
|
||||
import { TAdditionalPrivilegeDALFactory } from "../additional-privilege/additional-privilege-dal";
|
||||
import { TIdentityDALFactory } from "../identity/identity-dal";
|
||||
import { TMembershipRoleDALFactory } from "../membership/membership-role-dal";
|
||||
import { TOrgDALFactory } from "../org/org-dal";
|
||||
import { TRoleDALFactory } from "../role/role-dal";
|
||||
@@ -31,6 +32,7 @@ type TMembershipIdentityServiceFactoryDep = {
|
||||
>;
|
||||
orgDAL: Pick<TOrgDALFactory, "findById">;
|
||||
additionalPrivilegeDAL: Pick<TAdditionalPrivilegeDALFactory, "delete">;
|
||||
identityDAL: Pick<TIdentityDALFactory, "findById">;
|
||||
};
|
||||
|
||||
export type TMembershipIdentityServiceFactory = ReturnType<typeof membershipIdentityServiceFactory>;
|
||||
@@ -41,12 +43,14 @@ export const membershipIdentityServiceFactory = ({
|
||||
membershipRoleDAL,
|
||||
permissionService,
|
||||
orgDAL,
|
||||
additionalPrivilegeDAL
|
||||
additionalPrivilegeDAL,
|
||||
identityDAL
|
||||
}: TMembershipIdentityServiceFactoryDep) => {
|
||||
const scopeFactory = {
|
||||
[AccessScope.Organization]: newOrgMembershipIdentityFactory({
|
||||
orgDAL,
|
||||
permissionService
|
||||
permissionService,
|
||||
identityDAL
|
||||
}),
|
||||
[AccessScope.Project]: newProjectMembershipIdentityFactory({
|
||||
membershipIdentityDAL,
|
||||
@@ -305,7 +309,7 @@ export const membershipIdentityServiceFactory = ({
|
||||
[SearchResourceOperators.$contains]: dto.data.identityName
|
||||
}
|
||||
: undefined,
|
||||
role: dto.data.roles.length
|
||||
role: dto.data?.roles?.length
|
||||
? {
|
||||
[SearchResourceOperators.$in]: dto.data.roles
|
||||
}
|
||||
@@ -329,11 +333,29 @@ export const membershipIdentityServiceFactory = ({
|
||||
return membership;
|
||||
};
|
||||
|
||||
const listAvailableIdentities = async (dto: TListMembershipIdentityDTO) => {
|
||||
const { scopeData } = dto;
|
||||
const factory = scopeFactory[scopeData.scope];
|
||||
|
||||
await factory.onListMembershipIdentityGuard(dto);
|
||||
|
||||
const organizationDetails = await orgDAL.findById(dto.scopeData.orgId);
|
||||
if (!organizationDetails.rootOrgId) return { identities: [] };
|
||||
|
||||
const identities = await membershipIdentityDAL.listAvailableIdentities(
|
||||
organizationDetails.id,
|
||||
organizationDetails.rootOrgId
|
||||
);
|
||||
|
||||
return { identities };
|
||||
};
|
||||
|
||||
return {
|
||||
createMembership,
|
||||
updateMembership,
|
||||
deleteMembership,
|
||||
listMemberships,
|
||||
getMembershipByIdentityId
|
||||
getMembershipByIdentityId,
|
||||
listAvailableIdentities
|
||||
};
|
||||
};
|
||||
|
||||
@@ -54,14 +54,11 @@ export type TUpdateMembershipIdentityDTO = {
|
||||
export type TListMembershipIdentityDTO = {
|
||||
permission: OrgServiceActor;
|
||||
scopeData: AccessScopeData;
|
||||
selector: {
|
||||
identityId: string;
|
||||
};
|
||||
data: {
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
identityName?: string;
|
||||
roles: string[];
|
||||
roles?: string[];
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
|
||||
import { AccessScope, OrgMembershipRole } from "@app/db/schemas";
|
||||
import { AccessScope, OrganizationActionScope, OrgMembershipRole } from "@app/db/schemas";
|
||||
import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
|
||||
import {
|
||||
constructPermissionErrorMessage,
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
} from "@app/ee/services/permission/permission-fns";
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
||||
import { BadRequestError, InternalServerError, PermissionBoundaryError } from "@app/lib/errors";
|
||||
import { TIdentityDALFactory } from "@app/services/identity/identity-dal";
|
||||
import { TOrgDALFactory } from "@app/services/org/org-dal";
|
||||
import { isCustomOrgRole } from "@app/services/org/org-role-fns";
|
||||
|
||||
@@ -16,11 +17,13 @@ import { TMembershipIdentityScopeFactory } from "../membership-identity-types";
|
||||
type TOrgMembershipIdentityScopeFactoryDep = {
|
||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission" | "getOrgPermissionByRoles">;
|
||||
orgDAL: Pick<TOrgDALFactory, "findById">;
|
||||
identityDAL: Pick<TIdentityDALFactory, "findById">;
|
||||
};
|
||||
|
||||
export const newOrgMembershipIdentityFactory = ({
|
||||
permissionService,
|
||||
orgDAL
|
||||
orgDAL,
|
||||
identityDAL
|
||||
}: TOrgMembershipIdentityScopeFactoryDep): TMembershipIdentityScopeFactory => {
|
||||
const getScopeField: TMembershipIdentityScopeFactory["getScopeField"] = (dto) => {
|
||||
if (dto.scope === AccessScope.Organization) {
|
||||
@@ -38,23 +41,66 @@ export const newOrgMembershipIdentityFactory = ({
|
||||
|
||||
const isCustomRole: TMembershipIdentityScopeFactory["isCustomRole"] = (role: string) => isCustomOrgRole(role);
|
||||
|
||||
const onCreateMembershipIdentityGuard: TMembershipIdentityScopeFactory["onCreateMembershipIdentityGuard"] =
|
||||
async () => {
|
||||
throw new BadRequestError({
|
||||
message: "Organization membership cannot be created for organization scoped identity"
|
||||
const onCreateMembershipIdentityGuard: TMembershipIdentityScopeFactory["onCreateMembershipIdentityGuard"] = async (
|
||||
dto
|
||||
) => {
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
actor: dto.permission.type,
|
||||
actorId: dto.permission.id,
|
||||
orgId: dto.permission.orgId,
|
||||
actorAuthMethod: dto.permission.authMethod,
|
||||
actorOrgId: dto.permission.orgId,
|
||||
scope: OrganizationActionScope.ChildOrganization
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Create, OrgPermissionSubjects.Identity);
|
||||
|
||||
const identityDetails = await identityDAL.findById(dto.data.identityId);
|
||||
if (identityDetails.orgId !== dto.permission.rootOrgId) {
|
||||
throw new BadRequestError({ message: "Only identities from parent organization can be invited" });
|
||||
}
|
||||
|
||||
const permissionRoles = await permissionService.getOrgPermissionByRoles(
|
||||
dto.data.roles.map((el) => el.role),
|
||||
dto.permission.orgId
|
||||
);
|
||||
|
||||
const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(dto.permission.orgId);
|
||||
for (const permissionRole of permissionRoles) {
|
||||
if (permissionRole?.role?.name !== OrgMembershipRole.NoAccess) {
|
||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.GrantPrivileges,
|
||||
OrgPermissionSubjects.Identity,
|
||||
permission,
|
||||
permissionRole.permission
|
||||
);
|
||||
if (!permissionBoundary.isValid)
|
||||
throw new PermissionBoundaryError({
|
||||
message: constructPermissionErrorMessage(
|
||||
"Failed to update identity org membership",
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.GrantPrivileges,
|
||||
OrgPermissionSubjects.Identity
|
||||
),
|
||||
details: { missingPermissions: permissionBoundary.missingPermissions }
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const onUpdateMembershipIdentityGuard: TMembershipIdentityScopeFactory["onUpdateMembershipIdentityGuard"] = async (
|
||||
dto
|
||||
) => {
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
dto.permission.type,
|
||||
dto.permission.id,
|
||||
dto.permission.orgId,
|
||||
dto.permission.authMethod,
|
||||
dto.permission.orgId
|
||||
);
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
actor: dto.permission.type,
|
||||
actorId: dto.permission.id,
|
||||
orgId: dto.permission.orgId,
|
||||
actorAuthMethod: dto.permission.authMethod,
|
||||
actorOrgId: dto.permission.orgId,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
const permissionRoles = await permissionService.getOrgPermissionByRoles(
|
||||
dto.data.roles.map((el) => el.role),
|
||||
@@ -85,35 +131,54 @@ export const newOrgMembershipIdentityFactory = ({
|
||||
}
|
||||
};
|
||||
|
||||
const onDeleteMembershipIdentityGuard: TMembershipIdentityScopeFactory["onDeleteMembershipIdentityGuard"] =
|
||||
async () => {
|
||||
throw new BadRequestError({
|
||||
message: "Organization membership cannot be deleted for organization scoped identity"
|
||||
const onDeleteMembershipIdentityGuard: TMembershipIdentityScopeFactory["onDeleteMembershipIdentityGuard"] = async (
|
||||
dto
|
||||
) => {
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
actor: dto.permission.type,
|
||||
actorId: dto.permission.id,
|
||||
orgId: dto.permission.orgId,
|
||||
actorAuthMethod: dto.permission.authMethod,
|
||||
actorOrgId: dto.permission.orgId,
|
||||
scope: OrganizationActionScope.ChildOrganization
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Delete, OrgPermissionSubjects.Identity);
|
||||
|
||||
const identityDetails = await identityDAL.findById(dto.selector.identityId);
|
||||
if (identityDetails.orgId !== dto.permission.rootOrgId) {
|
||||
throw new BadRequestError({ message: "Only identities from parent organization can do this operation" });
|
||||
}
|
||||
|
||||
if (identityDetails.orgId === dto.permission.orgId) {
|
||||
throw new BadRequestError({ message: "Identity cannot exist as orphan" });
|
||||
}
|
||||
};
|
||||
|
||||
const onListMembershipIdentityGuard: TMembershipIdentityScopeFactory["onListMembershipIdentityGuard"] = async (
|
||||
dto
|
||||
) => {
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
dto.permission.type,
|
||||
dto.permission.id,
|
||||
dto.permission.orgId,
|
||||
dto.permission.authMethod,
|
||||
dto.permission.orgId
|
||||
);
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
actor: dto.permission.type,
|
||||
actorId: dto.permission.id,
|
||||
orgId: dto.permission.orgId,
|
||||
actorAuthMethod: dto.permission.authMethod,
|
||||
actorOrgId: dto.permission.orgId,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity);
|
||||
};
|
||||
|
||||
const onGetMembershipIdentityByIdentityIdGuard: TMembershipIdentityScopeFactory["onGetMembershipIdentityByIdentityIdGuard"] =
|
||||
async (dto) => {
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
dto.permission.type,
|
||||
dto.permission.id,
|
||||
dto.permission.orgId,
|
||||
dto.permission.authMethod,
|
||||
dto.permission.orgId
|
||||
);
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
actor: dto.permission.type,
|
||||
actorId: dto.permission.id,
|
||||
orgId: dto.permission.orgId,
|
||||
actorAuthMethod: dto.permission.authMethod,
|
||||
actorOrgId: dto.permission.orgId,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity);
|
||||
};
|
||||
|
||||
|
||||
@@ -291,5 +291,37 @@ export const membershipUserDALFactory = (db: TDbClient) => {
|
||||
}
|
||||
};
|
||||
|
||||
return { ...orm, findUsers, getUserById };
|
||||
// this right now only support sub organization
|
||||
const listAvailableUsers = async (orgId: string, rootOrgId: string) => {
|
||||
try {
|
||||
const usersConnectedToOrg = db
|
||||
.replicaNode()(TableName.Membership)
|
||||
.whereNotNull(`${TableName.Membership}.actorUserId`)
|
||||
.where(`${TableName.Membership}.scope`, AccessScope.Organization)
|
||||
.where(`${TableName.Membership}.scopeOrgId`, orgId)
|
||||
.select("actorUserId");
|
||||
|
||||
const docs = await db
|
||||
.replicaNode()(TableName.Membership)
|
||||
.join(TableName.Users, `${TableName.Users}.id`, `${TableName.Membership}.actorUserId`)
|
||||
.where(`${TableName.Membership}.scope`, AccessScope.Organization)
|
||||
.where(`${TableName.Users}.isGhost`, false)
|
||||
.whereNotNull(`${TableName.Membership}.actorUserId`)
|
||||
.where(`${TableName.Membership}.scopeOrgId`, rootOrgId)
|
||||
.whereNotIn(`${TableName.Membership}.actorUserId`, usersConnectedToOrg)
|
||||
.select(
|
||||
db.ref("id").withSchema(TableName.Users),
|
||||
db.ref("email").withSchema(TableName.Users),
|
||||
db.ref("username").withSchema(TableName.Users),
|
||||
db.ref("firstName").withSchema(TableName.Users),
|
||||
db.ref("lastName").withSchema(TableName.Users)
|
||||
);
|
||||
|
||||
return docs;
|
||||
} catch (error) {
|
||||
throw new DatabaseError({ error, name: "ListAvailableUsers" });
|
||||
}
|
||||
};
|
||||
|
||||
return { ...orm, findUsers, getUserById, listAvailableUsers };
|
||||
};
|
||||
|
||||
@@ -40,7 +40,7 @@ import { newProjectMembershipUserFactory } from "./project/project-membership-us
|
||||
type TMembershipUserServiceFactoryDep = {
|
||||
membershipUserDAL: TMembershipUserDALFactory;
|
||||
membershipRoleDAL: Pick<TMembershipRoleDALFactory, "insertMany" | "delete">;
|
||||
orgDAL: Pick<TOrgDALFactory, "findById" | "transaction">;
|
||||
orgDAL: Pick<TOrgDALFactory, "findById" | "transaction" | "find">;
|
||||
roleDAL: Pick<TRoleDALFactory, "find">;
|
||||
userDAL: TUserDALFactory;
|
||||
permissionService: Pick<
|
||||
@@ -83,7 +83,8 @@ export const membershipUserServiceFactory = ({
|
||||
orgDAL,
|
||||
tokenService,
|
||||
userDAL,
|
||||
userGroupMembershipDAL
|
||||
userGroupMembershipDAL,
|
||||
membershipUserDAL
|
||||
}),
|
||||
[AccessScope.Namespace]: newNamespaceMembershipUserFactory({}),
|
||||
[AccessScope.Project]: newProjectMembershipUserFactory({
|
||||
@@ -404,7 +405,7 @@ export const membershipUserServiceFactory = ({
|
||||
const membershipDoc = await membershipUserDAL.transaction(async (tx) => {
|
||||
if (dto.scopeData.scope === AccessScope.Organization) {
|
||||
const [doc] = await deleteOrgMembershipsFn({
|
||||
orgMembershipIds: [],
|
||||
orgMembershipIds: [existingMembership.id],
|
||||
orgId: dto.permission.orgId,
|
||||
orgDAL,
|
||||
projectKeyDAL,
|
||||
@@ -471,11 +472,26 @@ export const membershipUserServiceFactory = ({
|
||||
return membership;
|
||||
};
|
||||
|
||||
// Should only be used for sub organization as of now
|
||||
const listAvailableUsers = async (dto: TListMembershipUserDTO) => {
|
||||
const { scopeData } = dto;
|
||||
const factory = scopeFactory[scopeData.scope];
|
||||
|
||||
await factory.onListMembershipUserGuard(dto);
|
||||
|
||||
const organizationDetails = await orgDAL.findById(dto.scopeData.orgId);
|
||||
if (!organizationDetails.rootOrgId) return { users: [] };
|
||||
|
||||
const users = await membershipUserDAL.listAvailableUsers(organizationDetails.id, organizationDetails.rootOrgId);
|
||||
return { users };
|
||||
};
|
||||
|
||||
return {
|
||||
createMembership,
|
||||
updateMembership,
|
||||
deleteMembership,
|
||||
listMemberships,
|
||||
getMembershipByUserId
|
||||
getMembershipByUserId,
|
||||
listAvailableUsers
|
||||
};
|
||||
};
|
||||
|
||||
@@ -93,3 +93,8 @@ export type TGetMembershipUserByUserIdDTO = {
|
||||
userId: string;
|
||||
};
|
||||
};
|
||||
|
||||
export type TListAvailableUsersDTO = {
|
||||
permission: OrgServiceActor;
|
||||
scopeData: AccessScopeData;
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
|
||||
import { AccessScope } from "@app/db/schemas";
|
||||
import { AccessScope, OrganizationActionScope } from "@app/db/schemas";
|
||||
import { TUserGroupMembershipDALFactory } from "@app/ee/services/group/user-group-membership-dal";
|
||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
|
||||
@@ -15,6 +15,7 @@ import { isCustomOrgRole } from "@app/services/org/org-role-fns";
|
||||
import { SmtpTemplates, TSmtpService } from "@app/services/smtp/smtp-service";
|
||||
import { TUserDALFactory } from "@app/services/user/user-dal";
|
||||
|
||||
import { TMembershipUserDALFactory } from "../membership-user-dal";
|
||||
import { TMembershipUserScopeFactory } from "../membership-user-types";
|
||||
|
||||
type TOrgMembershipUserScopeFactoryDep = {
|
||||
@@ -25,6 +26,7 @@ type TOrgMembershipUserScopeFactoryDep = {
|
||||
orgDAL: Pick<TOrgDALFactory, "findById">;
|
||||
userGroupMembershipDAL: Pick<TUserGroupMembershipDALFactory, "delete">;
|
||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||
membershipUserDAL: Pick<TMembershipUserDALFactory, "find">;
|
||||
};
|
||||
|
||||
export const newOrgMembershipUserFactory = ({
|
||||
@@ -33,7 +35,8 @@ export const newOrgMembershipUserFactory = ({
|
||||
userDAL,
|
||||
orgDAL,
|
||||
smtpService,
|
||||
licenseService
|
||||
licenseService,
|
||||
membershipUserDAL
|
||||
}: TOrgMembershipUserScopeFactoryDep): TMembershipUserScopeFactory => {
|
||||
const getScopeField: TMembershipUserScopeFactory["getScopeField"] = (dto) => {
|
||||
if (dto.scope === AccessScope.Organization) {
|
||||
@@ -51,14 +54,18 @@ export const newOrgMembershipUserFactory = ({
|
||||
|
||||
const isCustomRole: TMembershipUserScopeFactory["isCustomRole"] = (role: string) => isCustomOrgRole(role);
|
||||
|
||||
const onCreateMembershipUserGuard: TMembershipUserScopeFactory["onCreateMembershipUserGuard"] = async (dto) => {
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
dto.permission.type,
|
||||
dto.permission.id,
|
||||
dto.permission.orgId,
|
||||
dto.permission.authMethod,
|
||||
dto.permission.orgId
|
||||
);
|
||||
const onCreateMembershipUserGuard: TMembershipUserScopeFactory["onCreateMembershipUserGuard"] = async (
|
||||
dto,
|
||||
newMembers
|
||||
) => {
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
actor: dto.permission.type,
|
||||
actorId: dto.permission.id,
|
||||
orgId: dto.permission.orgId,
|
||||
actorAuthMethod: dto.permission.authMethod,
|
||||
actorOrgId: dto.permission.orgId,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Member);
|
||||
|
||||
const plan = await licenseService.getPlan(dto.permission.orgId);
|
||||
@@ -77,6 +84,25 @@ export const newOrgMembershipUserFactory = ({
|
||||
message: "Failed to invite user due to org-level auth enforced for organization"
|
||||
});
|
||||
}
|
||||
|
||||
if (org.rootOrgId) {
|
||||
const rootOrgMembership = await membershipUserDAL.find({
|
||||
scope: AccessScope.Organization,
|
||||
$in: {
|
||||
actorUserId: newMembers.map((el) => el.id)
|
||||
},
|
||||
scopeOrgId: org.rootOrgId
|
||||
});
|
||||
if (rootOrgMembership.length !== newMembers.length) {
|
||||
const emails = newMembers
|
||||
.filter((user) => !rootOrgMembership.find((i) => i.actorUserId === user.id))
|
||||
.map((el) => el.email)
|
||||
.join(",");
|
||||
throw new BadRequestError({
|
||||
message: `Users with email ${emails} doesn't have membership in root organization`
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const onCreateMembershipComplete: TMembershipUserScopeFactory["onCreateMembershipComplete"] = async (
|
||||
@@ -95,7 +121,18 @@ export const newOrgMembershipUserFactory = ({
|
||||
|
||||
const signUpTokens: { email: string; link: string }[] = [];
|
||||
const orgDetails = await orgDAL.findById(dto.permission.orgId);
|
||||
|
||||
if (orgDetails.rootOrgId) {
|
||||
const emails = newUsers.map((el) => el.email).filter(Boolean);
|
||||
await smtpService.sendMail({
|
||||
template: SmtpTemplates.SubOrgInvite,
|
||||
subjectLine: "Infisical sub-organization invitation",
|
||||
recipients: emails as string[],
|
||||
substitutions: {
|
||||
subOrganizationName: orgDetails.slug,
|
||||
callback_url: `${appCfg.SITE_URL}/organization/projects?subOrganization=${orgDetails.slug}`
|
||||
}
|
||||
});
|
||||
} else {
|
||||
await Promise.allSettled(
|
||||
newUsers.map(async (el) => {
|
||||
const token = await tokenService.createTokenForUser({
|
||||
@@ -129,53 +166,58 @@ export const newOrgMembershipUserFactory = ({
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
return { signUpTokens };
|
||||
};
|
||||
|
||||
const onUpdateMembershipUserGuard: TMembershipUserScopeFactory["onUpdateMembershipUserGuard"] = async (dto) => {
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
dto.permission.type,
|
||||
dto.permission.id,
|
||||
dto.permission.orgId,
|
||||
dto.permission.authMethod,
|
||||
dto.permission.orgId
|
||||
);
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
actor: dto.permission.type,
|
||||
actorId: dto.permission.id,
|
||||
orgId: dto.permission.orgId,
|
||||
actorAuthMethod: dto.permission.authMethod,
|
||||
actorOrgId: dto.permission.orgId,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Member);
|
||||
};
|
||||
|
||||
const onDeleteMembershipUserGuard: TMembershipUserScopeFactory["onDeleteMembershipUserGuard"] = async (dto) => {
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
dto.permission.type,
|
||||
dto.permission.id,
|
||||
dto.permission.orgId,
|
||||
dto.permission.authMethod,
|
||||
dto.permission.orgId
|
||||
);
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
actor: dto.permission.type,
|
||||
actorId: dto.permission.id,
|
||||
orgId: dto.permission.orgId,
|
||||
actorAuthMethod: dto.permission.authMethod,
|
||||
actorOrgId: dto.permission.orgId,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Delete, OrgPermissionSubjects.Member);
|
||||
};
|
||||
|
||||
const onListMembershipUserGuard: TMembershipUserScopeFactory["onListMembershipUserGuard"] = async (dto) => {
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
dto.permission.type,
|
||||
dto.permission.id,
|
||||
dto.permission.orgId,
|
||||
dto.permission.authMethod,
|
||||
dto.permission.orgId
|
||||
);
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
actor: dto.permission.type,
|
||||
actorId: dto.permission.id,
|
||||
orgId: dto.permission.orgId,
|
||||
actorAuthMethod: dto.permission.authMethod,
|
||||
actorOrgId: dto.permission.orgId,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Member);
|
||||
};
|
||||
|
||||
const onGetMembershipUserByUserIdGuard: TMembershipUserScopeFactory["onGetMembershipUserByUserIdGuard"] = async (
|
||||
dto
|
||||
) => {
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
dto.permission.type,
|
||||
dto.permission.id,
|
||||
dto.permission.orgId,
|
||||
dto.permission.authMethod,
|
||||
dto.permission.orgId
|
||||
);
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
actor: dto.permission.type,
|
||||
actorId: dto.permission.id,
|
||||
orgId: dto.permission.orgId,
|
||||
actorAuthMethod: dto.permission.authMethod,
|
||||
actorOrgId: dto.permission.orgId,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Member);
|
||||
};
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
import { CronJob } from "cron";
|
||||
import { FastifyReply, FastifyRequest } from "fastify";
|
||||
|
||||
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 { BadRequestError, DatabaseError, NotFoundError } from "@app/lib/errors";
|
||||
@@ -208,13 +209,14 @@ export const microsoftTeamsServiceFactory = ({
|
||||
});
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
actor,
|
||||
actorId,
|
||||
microsoftTeamsIntegration.orgId,
|
||||
orgId: microsoftTeamsIntegration.orgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
actorOrgId,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Settings);
|
||||
|
||||
@@ -282,13 +284,14 @@ export const microsoftTeamsServiceFactory = ({
|
||||
description,
|
||||
redirectUri
|
||||
}: TCreateMicrosoftTeamsIntegrationDTO) => {
|
||||
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(OrgPermissionActions.Create, OrgPermissionSubjects.Settings);
|
||||
|
||||
@@ -393,13 +396,14 @@ export const microsoftTeamsServiceFactory = ({
|
||||
});
|
||||
};
|
||||
const getClientId = async ({ actorId, actor, actorOrgId, actorAuthMethod }: TGetClientIdDTO) => {
|
||||
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(OrgPermissionActions.Read, OrgPermissionSubjects.Settings);
|
||||
|
||||
@@ -427,13 +431,14 @@ export const microsoftTeamsServiceFactory = ({
|
||||
actorOrgId,
|
||||
actorAuthMethod
|
||||
}: TGetMicrosoftTeamsIntegrationByOrgDTO) => {
|
||||
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(OrgPermissionActions.Create, OrgPermissionSubjects.Settings);
|
||||
|
||||
@@ -463,13 +468,14 @@ export const microsoftTeamsServiceFactory = ({
|
||||
});
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
actor,
|
||||
actorId,
|
||||
microsoftTeamsIntegration.orgId,
|
||||
orgId: microsoftTeamsIntegration.orgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
actorOrgId,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Settings);
|
||||
|
||||
@@ -495,13 +501,14 @@ export const microsoftTeamsServiceFactory = ({
|
||||
});
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
actor,
|
||||
actorId,
|
||||
microsoftTeamsIntegration.orgId,
|
||||
orgId: microsoftTeamsIntegration.orgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
actorOrgId,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Settings);
|
||||
|
||||
@@ -549,13 +556,14 @@ export const microsoftTeamsServiceFactory = ({
|
||||
});
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
actor,
|
||||
actorId,
|
||||
microsoftTeamsIntegration.orgId,
|
||||
orgId: microsoftTeamsIntegration.orgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
actorOrgId,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Delete, OrgPermissionSubjects.Settings);
|
||||
|
||||
@@ -577,13 +585,14 @@ export const microsoftTeamsServiceFactory = ({
|
||||
});
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
actor,
|
||||
actorId,
|
||||
microsoftTeamsIntegration.orgId,
|
||||
orgId: microsoftTeamsIntegration.orgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
actorOrgId,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Settings);
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user