mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-17 08:35:52 +00:00
feat: fixing ts
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
import { TAuthTokens, TAuthTokenSessions } from "@app/db/schemas";
|
||||
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 { TOrgMembershipDALFactory } from "@app/services/org-membership/org-membership-dal";
|
||||
|
||||
import { AuthModeJwtTokenPayload, AuthModeRefreshJwtTokenPayload, AuthTokenType } from "../auth/auth-type";
|
||||
import { TMembershipUserDALFactory } from "../membership-user/membership-user-dal";
|
||||
import { TUserDALFactory } from "../user/user-dal";
|
||||
import { TTokenDALFactory } from "./auth-token-dal";
|
||||
import { TCreateTokenForUserDTO, TIssueAuthTokenDTO, TokenType, TValidateTokenForUserDTO } from "./auth-token-types";
|
||||
@@ -14,7 +14,7 @@ import { TCreateTokenForUserDTO, TIssueAuthTokenDTO, TokenType, TValidateTokenFo
|
||||
type TAuthTokenServiceFactoryDep = {
|
||||
tokenDAL: TTokenDALFactory;
|
||||
userDAL: Pick<TUserDALFactory, "findById" | "transaction">;
|
||||
orgMembershipDAL: Pick<TOrgMembershipDALFactory, "findOne">;
|
||||
membershipUserDAL: Pick<TMembershipUserDALFactory, "findOne">;
|
||||
};
|
||||
|
||||
export type TAuthTokenServiceFactory = ReturnType<typeof tokenServiceFactory>;
|
||||
@@ -80,7 +80,7 @@ export const getTokenConfig = (tokenType: TokenType) => {
|
||||
}
|
||||
};
|
||||
|
||||
export const tokenServiceFactory = ({ tokenDAL, userDAL, orgMembershipDAL }: TAuthTokenServiceFactoryDep) => {
|
||||
export const tokenServiceFactory = ({ tokenDAL, userDAL, membershipUserDAL }: TAuthTokenServiceFactoryDep) => {
|
||||
const createTokenForUser = async ({ type, userId, orgId, aliasId, payload }: TCreateTokenForUserDTO) => {
|
||||
const { token, ...tkCfg } = getTokenConfig(type);
|
||||
const appCfg = getConfig();
|
||||
@@ -208,9 +208,10 @@ export const tokenServiceFactory = ({ tokenDAL, userDAL, orgMembershipDAL }: TAu
|
||||
if (!user || !user.isAccepted) throw new NotFoundError({ message: `User with ID '${session.userId}' not found` });
|
||||
|
||||
if (token.organizationId) {
|
||||
const orgMembership = await orgMembershipDAL.findOne({
|
||||
userId: user.id,
|
||||
orgId: token.organizationId
|
||||
const orgMembership = await membershipUserDAL.findOne({
|
||||
actorUserId: user.id,
|
||||
scopeOrgId: token.organizationId,
|
||||
scope: AccessScope.Organization
|
||||
});
|
||||
|
||||
if (!orgMembership) {
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
import { OrgMembershipRole, OrgMembershipStatus, TableName, TUsers, UserDeviceSchema } from "@app/db/schemas";
|
||||
import {
|
||||
AccessScope,
|
||||
OrgMembershipRole,
|
||||
OrgMembershipStatus,
|
||||
TableName,
|
||||
TUsers,
|
||||
UserDeviceSchema
|
||||
} from "@app/db/schemas";
|
||||
import { EventType, TAuditLogServiceFactory } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { isAuthMethodSaml } from "@app/ee/services/permission/permission-fns";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
@@ -14,11 +21,12 @@ import { getServerCfg } from "@app/services/super-admin/super-admin-service";
|
||||
|
||||
import { TAuthTokenServiceFactory } from "../auth-token/auth-token-service";
|
||||
import { TokenType } from "../auth-token/auth-token-types";
|
||||
import { TMembershipRoleDALFactory } from "../membership/membership-role-dal";
|
||||
import { TMembershipUserDALFactory } from "../membership-user/membership-user-dal";
|
||||
import { TNotificationServiceFactory } from "../notification/notification-service";
|
||||
import { NotificationType } from "../notification/notification-types";
|
||||
import { TOrgDALFactory } from "../org/org-dal";
|
||||
import { getDefaultOrgMembershipRole } from "../org/org-role-fns";
|
||||
import { TOrgMembershipDALFactory } from "../org-membership/org-membership-dal";
|
||||
import { SmtpTemplates, TSmtpService } from "../smtp/smtp-service";
|
||||
import { LoginMethod } from "../super-admin/super-admin-types";
|
||||
import { TTotpServiceFactory } from "../totp/totp-service";
|
||||
@@ -48,7 +56,8 @@ type TAuthLoginServiceFactoryDep = {
|
||||
smtpService: TSmtpService;
|
||||
totpService: Pick<TTotpServiceFactory, "verifyUserTotp" | "verifyWithUserRecoveryCode">;
|
||||
auditLogService: Pick<TAuditLogServiceFactory, "createAuditLog">;
|
||||
orgMembershipDAL: TOrgMembershipDALFactory;
|
||||
membershipUserDAL: TMembershipUserDALFactory;
|
||||
membershipRoleDAL: TMembershipRoleDALFactory;
|
||||
notificationService: Pick<TNotificationServiceFactory, "createUserNotifications">;
|
||||
};
|
||||
|
||||
@@ -58,10 +67,11 @@ export const authLoginServiceFactory = ({
|
||||
tokenService,
|
||||
smtpService,
|
||||
orgDAL,
|
||||
orgMembershipDAL,
|
||||
totpService,
|
||||
auditLogService,
|
||||
notificationService
|
||||
notificationService,
|
||||
membershipUserDAL,
|
||||
membershipRoleDAL
|
||||
}: TAuthLoginServiceFactoryDep) => {
|
||||
/*
|
||||
* Private
|
||||
@@ -163,8 +173,8 @@ export const authLoginServiceFactory = ({
|
||||
if (organizationId) {
|
||||
const org = await orgDAL.findById(organizationId);
|
||||
if (org) {
|
||||
await orgMembershipDAL.update(
|
||||
{ userId: user.id, orgId: org.id },
|
||||
await membershipUserDAL.update(
|
||||
{ actorUserId: user.id, scopeOrgId: org.id, scope: AccessScope.Organization },
|
||||
{ lastLoginAuthMethod: authMethod, lastLoginTime: new Date() }
|
||||
);
|
||||
if (org.userTokenExpiration) {
|
||||
@@ -858,21 +868,34 @@ export const authLoginServiceFactory = ({
|
||||
}
|
||||
orgId = defaultOrg.id;
|
||||
const [orgMembership] = await orgDAL.findMembership({
|
||||
[`${TableName.OrgMembership}.userId` as "userId"]: user.id,
|
||||
[`${TableName.OrgMembership}.orgId` as "id"]: orgId
|
||||
[`${TableName.Membership}.actorUserId` as "actorUserId"]: user.id,
|
||||
[`${TableName.Membership}.scopeOrgId` as "scopeOrgId"]: orgId,
|
||||
[`${TableName.Membership}.scope` as "scope"]: AccessScope.Organization
|
||||
});
|
||||
|
||||
if (!orgMembership) {
|
||||
const { role, roleId } = await getDefaultOrgMembershipRole(defaultOrg.defaultMembershipRole);
|
||||
|
||||
await orgMembershipDAL.create({
|
||||
userId: user.id,
|
||||
inviteEmail: email,
|
||||
orgId,
|
||||
role,
|
||||
roleId,
|
||||
status: OrgMembershipStatus.Accepted,
|
||||
isActive: true
|
||||
await membershipUserDAL.transaction(async (tx) => {
|
||||
const membership = await membershipUserDAL.create(
|
||||
{
|
||||
actorUserId: user?.id,
|
||||
inviteEmail: email,
|
||||
scopeOrgId: orgId,
|
||||
scope: AccessScope.Organization,
|
||||
status: OrgMembershipStatus.Accepted,
|
||||
isActive: true
|
||||
},
|
||||
tx
|
||||
);
|
||||
await membershipRoleDAL.create(
|
||||
{
|
||||
membershipId: membership.id,
|
||||
role,
|
||||
customRoleId: roleId
|
||||
},
|
||||
tx
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -895,10 +918,11 @@ export const authLoginServiceFactory = ({
|
||||
if (org) {
|
||||
// checks for the membership and only sets the orgId / orgName if the user is a member of the specified org
|
||||
const orgMembership = await orgDAL.findMembership({
|
||||
[`${TableName.OrgMembership}.userId` as "userId"]: user.id,
|
||||
[`${TableName.OrgMembership}.orgId` as "orgId"]: org.id,
|
||||
[`${TableName.OrgMembership}.isActive` as "isActive"]: true,
|
||||
[`${TableName.OrgMembership}.status` as "status"]: OrgMembershipStatus.Accepted
|
||||
[`${TableName.Membership}.actorUserId` as "actorUserId"]: user.id,
|
||||
[`${TableName.Membership}.scopeOrgId` as "scopeOrgId"]: org.id,
|
||||
[`${TableName.Membership}.isActive` as "isActive"]: true,
|
||||
[`${TableName.Membership}.status` as "status"]: OrgMembershipStatus.Accepted,
|
||||
[`${TableName.Membership}.scope` as "scope"]: AccessScope.Organization
|
||||
});
|
||||
|
||||
if (orgMembership) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { OrgMembershipStatus, TableName } from "@app/db/schemas";
|
||||
import { AccessScope, OrgMembershipStatus, TableName } from "@app/db/schemas";
|
||||
import { convertPendingGroupAdditionsToGroupMemberships } from "@app/ee/services/group/group-fns";
|
||||
import { TUserGroupMembershipDALFactory } from "@app/ee/services/group/user-group-membership-dal";
|
||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||
@@ -8,17 +8,15 @@ import { crypto } from "@app/lib/crypto/cryptography";
|
||||
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
||||
import { getMinExpiresIn } from "@app/lib/fn";
|
||||
import { isDisposableEmail } from "@app/lib/validator";
|
||||
import { TGroupProjectDALFactory } from "@app/services/group-project/group-project-dal";
|
||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||
import { TProjectBotDALFactory } from "@app/services/project-bot/project-bot-dal";
|
||||
import { TProjectKeyDALFactory } from "@app/services/project-key/project-key-dal";
|
||||
|
||||
import { TAuthTokenServiceFactory } from "../auth-token/auth-token-service";
|
||||
import { TokenType } from "../auth-token/auth-token-types";
|
||||
import { TMembershipGroupDALFactory } from "../membership-group/membership-group-dal";
|
||||
import { TOrgDALFactory } from "../org/org-dal";
|
||||
import { TOrgServiceFactory } from "../org/org-service";
|
||||
import { TProjectMembershipDALFactory } from "../project-membership/project-membership-dal";
|
||||
import { TProjectUserMembershipRoleDALFactory } from "../project-membership/project-user-membership-role-dal";
|
||||
import { SmtpTemplates, TSmtpService } from "../smtp/smtp-service";
|
||||
import { getServerCfg } from "../super-admin/super-admin-service";
|
||||
import { TUserDALFactory } from "../user/user-dal";
|
||||
@@ -42,14 +40,12 @@ type TAuthSignupDep = {
|
||||
projectKeyDAL: Pick<TProjectKeyDALFactory, "find" | "findLatestProjectKey" | "insertMany">;
|
||||
projectDAL: Pick<TProjectDALFactory, "findProjectGhostUser" | "findProjectById" | "findById">;
|
||||
projectBotDAL: Pick<TProjectBotDALFactory, "findOne">;
|
||||
groupProjectDAL: Pick<TGroupProjectDALFactory, "find">;
|
||||
orgService: Pick<TOrgServiceFactory, "createOrganization" | "findOrganizationById">;
|
||||
orgDAL: TOrgDALFactory;
|
||||
tokenService: TAuthTokenServiceFactory;
|
||||
smtpService: TSmtpService;
|
||||
licenseService: Pick<TLicenseServiceFactory, "updateSubscriptionOrgMemberCount">;
|
||||
projectMembershipDAL: Pick<TProjectMembershipDALFactory, "find" | "transaction" | "insertMany">;
|
||||
projectUserMembershipRoleDAL: Pick<TProjectUserMembershipRoleDALFactory, "insertMany">;
|
||||
membershipGroupDAL: TMembershipGroupDALFactory;
|
||||
};
|
||||
|
||||
export type TAuthSignupFactory = ReturnType<typeof authSignupServiceFactory>;
|
||||
@@ -60,11 +56,11 @@ export const authSignupServiceFactory = ({
|
||||
projectKeyDAL,
|
||||
projectDAL,
|
||||
projectBotDAL,
|
||||
groupProjectDAL,
|
||||
tokenService,
|
||||
smtpService,
|
||||
orgService,
|
||||
orgDAL,
|
||||
membershipGroupDAL,
|
||||
licenseService
|
||||
}: TAuthSignupDep) => {
|
||||
// first step of signup. create user and send email
|
||||
@@ -200,9 +196,10 @@ export const authSignupServiceFactory = ({
|
||||
organizationId
|
||||
) {
|
||||
const [pendingOrgMembership] = await orgDAL.findMembership({
|
||||
[`${TableName.OrgMembership}.userId` as "userId"]: user.id,
|
||||
[`${TableName.Membership}.actorUserId` as "actorUserId"]: user.id,
|
||||
status: OrgMembershipStatus.Invited,
|
||||
[`${TableName.OrgMembership}.orgId` as "orgId"]: organizationId
|
||||
[`${TableName.Membership}.scopeOrgId` as "scopeOrgId"]: organizationId,
|
||||
[`${TableName.Membership}.scope` as "scope"]: AccessScope.Organization
|
||||
});
|
||||
|
||||
if (pendingOrgMembership) {
|
||||
@@ -241,18 +238,18 @@ export const authSignupServiceFactory = ({
|
||||
}
|
||||
|
||||
const updatedMembersips = await orgDAL.updateMembership(
|
||||
{ inviteEmail: sanitizedEmail, status: OrgMembershipStatus.Invited },
|
||||
{ userId: user.id, status: OrgMembershipStatus.Accepted }
|
||||
{ inviteEmail: sanitizedEmail, status: OrgMembershipStatus.Invited, scope: AccessScope.Organization },
|
||||
{ actorUserId: user.id, status: OrgMembershipStatus.Accepted }
|
||||
);
|
||||
const uniqueOrgId = [...new Set(updatedMembersips.map(({ orgId }) => orgId))];
|
||||
const uniqueOrgId = [...new Set(updatedMembersips.map(({ scopeOrgId }) => scopeOrgId))];
|
||||
await Promise.allSettled(uniqueOrgId.map((orgId) => licenseService.updateSubscriptionOrgMemberCount(orgId)));
|
||||
|
||||
await convertPendingGroupAdditionsToGroupMemberships({
|
||||
userIds: [user.id],
|
||||
userDAL,
|
||||
userGroupMembershipDAL,
|
||||
groupProjectDAL,
|
||||
projectKeyDAL,
|
||||
membershipGroupDAL,
|
||||
projectDAL,
|
||||
projectBotDAL
|
||||
});
|
||||
@@ -351,21 +348,21 @@ export const authSignupServiceFactory = ({
|
||||
);
|
||||
|
||||
const updatedMembersips = await orgDAL.updateMembership(
|
||||
{ inviteEmail: sanitizedEmail, status: OrgMembershipStatus.Invited },
|
||||
{ userId: us.id, status: OrgMembershipStatus.Accepted },
|
||||
{ inviteEmail: sanitizedEmail, status: OrgMembershipStatus.Invited, scope: AccessScope.Organization },
|
||||
{ actorUserId: us.id, status: OrgMembershipStatus.Accepted },
|
||||
tx
|
||||
);
|
||||
const uniqueOrgId = [...new Set(updatedMembersips.map(({ orgId }) => orgId))];
|
||||
const uniqueOrgId = [...new Set(updatedMembersips.map(({ scopeOrgId }) => scopeOrgId))];
|
||||
await Promise.allSettled(uniqueOrgId.map((orgId) => licenseService.updateSubscriptionOrgMemberCount(orgId, tx)));
|
||||
|
||||
await convertPendingGroupAdditionsToGroupMemberships({
|
||||
userIds: [user.id],
|
||||
userDAL,
|
||||
userGroupMembershipDAL,
|
||||
groupProjectDAL,
|
||||
projectKeyDAL,
|
||||
projectDAL,
|
||||
projectBotDAL,
|
||||
membershipGroupDAL,
|
||||
tx
|
||||
});
|
||||
|
||||
|
||||
+6
-6
@@ -1,19 +1,19 @@
|
||||
import { OrgMembershipRole, TOrgRoles } from "@app/db/schemas";
|
||||
import { OrgMembershipRole, TRoles } from "@app/db/schemas";
|
||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
||||
import { TOrgRoleDALFactory } from "@app/services/org/org-role-dal";
|
||||
import { isCustomOrgRole } from "@app/services/org/org-role-fns";
|
||||
|
||||
import { TRoleDALFactory } from "../role/role-dal";
|
||||
import { TExternalGroupOrgMembershipRoleMappingDTO } from "./external-group-org-role-mapping-types";
|
||||
|
||||
export const constructGroupOrgMembershipRoleMappings = async ({
|
||||
mappingsDTO,
|
||||
orgId,
|
||||
orgRoleDAL,
|
||||
roleDAL,
|
||||
licenseService
|
||||
}: {
|
||||
mappingsDTO: TExternalGroupOrgMembershipRoleMappingDTO[];
|
||||
orgRoleDAL: TOrgRoleDALFactory;
|
||||
roleDAL: TRoleDALFactory;
|
||||
licenseService: TLicenseServiceFactory;
|
||||
orgId: string;
|
||||
}) => {
|
||||
@@ -30,9 +30,9 @@ export const constructGroupOrgMembershipRoleMappings = async ({
|
||||
.filter((mapping) => isCustomOrgRole(mapping.roleSlug))
|
||||
.map((mapping) => mapping.roleSlug);
|
||||
|
||||
let customRolesMap: Map<string, TOrgRoles> = new Map();
|
||||
let customRolesMap: Map<string, TRoles> = new Map();
|
||||
if (customRoleSlugs.length > 0) {
|
||||
const customRoles = await orgRoleDAL.find({
|
||||
const customRoles = await roleDAL.find({
|
||||
orgId,
|
||||
$in: {
|
||||
slug: customRoleSlugs
|
||||
|
||||
+4
-4
@@ -6,15 +6,15 @@ import { TPermissionServiceFactory } from "@app/ee/services/permission/permissio
|
||||
import { OrgServiceActor } from "@app/lib/types";
|
||||
import { constructGroupOrgMembershipRoleMappings } from "@app/services/external-group-org-role-mapping/external-group-org-role-mapping-fns";
|
||||
import { TSyncExternalGroupOrgMembershipRoleMappingsDTO } from "@app/services/external-group-org-role-mapping/external-group-org-role-mapping-types";
|
||||
import { TOrgRoleDALFactory } from "@app/services/org/org-role-dal";
|
||||
|
||||
import { TRoleDALFactory } from "../role/role-dal";
|
||||
import { TExternalGroupOrgRoleMappingDALFactory } from "./external-group-org-role-mapping-dal";
|
||||
|
||||
type TExternalGroupOrgRoleMappingServiceFactoryDep = {
|
||||
externalGroupOrgRoleMappingDAL: TExternalGroupOrgRoleMappingDALFactory;
|
||||
permissionService: TPermissionServiceFactory;
|
||||
licenseService: TLicenseServiceFactory;
|
||||
orgRoleDAL: TOrgRoleDALFactory;
|
||||
roleDAL: TRoleDALFactory;
|
||||
};
|
||||
|
||||
export type TExternalGroupOrgRoleMappingServiceFactory = ReturnType<typeof externalGroupOrgRoleMappingServiceFactory>;
|
||||
@@ -23,7 +23,7 @@ export const externalGroupOrgRoleMappingServiceFactory = ({
|
||||
externalGroupOrgRoleMappingDAL,
|
||||
licenseService,
|
||||
permissionService,
|
||||
orgRoleDAL
|
||||
roleDAL
|
||||
}: TExternalGroupOrgRoleMappingServiceFactoryDep) => {
|
||||
const listExternalGroupOrgRoleMappings = async (actor: OrgServiceActor) => {
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
@@ -61,7 +61,7 @@ export const externalGroupOrgRoleMappingServiceFactory = ({
|
||||
|
||||
const mappings = await constructGroupOrgMembershipRoleMappings({
|
||||
mappingsDTO: dto.mappings,
|
||||
orgRoleDAL,
|
||||
roleDAL,
|
||||
licenseService,
|
||||
orgId: actor.orgId
|
||||
});
|
||||
|
||||
@@ -1,504 +1,27 @@
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
|
||||
import { ActionProjectType, ProjectMembershipRole, ProjectVersion, SecretKeyEncoding, TGroups } from "@app/db/schemas";
|
||||
import { ActionProjectType } from "@app/db/schemas";
|
||||
import { TListProjectGroupUsersDTO } from "@app/ee/services/group/group-types";
|
||||
import {
|
||||
constructPermissionErrorMessage,
|
||||
validatePrivilegeChangeOperation
|
||||
} from "@app/ee/services/permission/permission-fns";
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
||||
import { ProjectPermissionGroupActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||
import { crypto } from "@app/lib/crypto/cryptography";
|
||||
import { BadRequestError, NotFoundError, PermissionBoundaryError } from "@app/lib/errors";
|
||||
import { groupBy } from "@app/lib/fn";
|
||||
import { ms } from "@app/lib/ms";
|
||||
import { isUuidV4 } from "@app/lib/validator";
|
||||
import { NotFoundError } from "@app/lib/errors";
|
||||
|
||||
import { TGroupDALFactory } from "../../ee/services/group/group-dal";
|
||||
import { TUserGroupMembershipDALFactory } from "../../ee/services/group/user-group-membership-dal";
|
||||
import { TProjectDALFactory } from "../project/project-dal";
|
||||
import { TProjectBotDALFactory } from "../project-bot/project-bot-dal";
|
||||
import { TProjectKeyDALFactory } from "../project-key/project-key-dal";
|
||||
import { ProjectUserMembershipTemporaryMode } from "../project-membership/project-membership-types";
|
||||
import { TProjectRoleDALFactory } from "../project-role/project-role-dal";
|
||||
import { TGroupProjectDALFactory } from "./group-project-dal";
|
||||
import { TGroupProjectMembershipRoleDALFactory } from "./group-project-membership-role-dal";
|
||||
import {
|
||||
TCreateProjectGroupDTO,
|
||||
TDeleteProjectGroupDTO,
|
||||
TGetGroupInProjectDTO,
|
||||
TListProjectGroupDTO,
|
||||
TUpdateProjectGroupDTO
|
||||
} from "./group-project-types";
|
||||
|
||||
type TGroupProjectServiceFactoryDep = {
|
||||
groupProjectDAL: Pick<TGroupProjectDALFactory, "findOne" | "transaction" | "create" | "delete" | "findByProjectId">;
|
||||
groupProjectMembershipRoleDAL: Pick<
|
||||
TGroupProjectMembershipRoleDALFactory,
|
||||
"create" | "transaction" | "insertMany" | "delete"
|
||||
>;
|
||||
userGroupMembershipDAL: Pick<TUserGroupMembershipDALFactory, "findGroupMembersNotInProject" | "find">;
|
||||
projectDAL: Pick<TProjectDALFactory, "findOne" | "findProjectGhostUser" | "findById">;
|
||||
projectKeyDAL: Pick<TProjectKeyDALFactory, "findLatestProjectKey" | "delete" | "insertMany" | "transaction">;
|
||||
projectRoleDAL: Pick<TProjectRoleDALFactory, "find">;
|
||||
projectBotDAL: TProjectBotDALFactory;
|
||||
groupDAL: Pick<TGroupDALFactory, "findOne" | "findAllGroupPossibleMembers">;
|
||||
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission" | "getProjectPermissionByRoles" | "invalidateProjectPermissionCache">;
|
||||
projectDAL: Pick<TProjectDALFactory, "findOne" | "findProjectGhostUser" | "findById">;
|
||||
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission" | "getProjectPermissionByRoles">;
|
||||
};
|
||||
|
||||
export type TGroupProjectServiceFactory = ReturnType<typeof groupProjectServiceFactory>;
|
||||
|
||||
export const groupProjectServiceFactory = ({
|
||||
groupDAL,
|
||||
groupProjectDAL,
|
||||
groupProjectMembershipRoleDAL,
|
||||
userGroupMembershipDAL,
|
||||
projectDAL,
|
||||
projectKeyDAL,
|
||||
projectBotDAL,
|
||||
projectRoleDAL,
|
||||
permissionService
|
||||
}: TGroupProjectServiceFactoryDep) => {
|
||||
const addGroupToProject = async ({
|
||||
actor,
|
||||
actorId,
|
||||
actorOrgId,
|
||||
actorAuthMethod,
|
||||
roles,
|
||||
projectId,
|
||||
groupIdOrName
|
||||
}: TCreateProjectGroupDTO) => {
|
||||
const project = await projectDAL.findById(projectId);
|
||||
|
||||
if (!project) throw new NotFoundError({ message: `Failed to find project with ID ${projectId}` });
|
||||
if (project.version < 2) throw new BadRequestError({ message: `Failed to add group to E2EE project` });
|
||||
|
||||
const { permission, membership } = await permissionService.getProjectPermission({
|
||||
actor,
|
||||
actorId,
|
||||
projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId,
|
||||
actionProjectType: ActionProjectType.Any
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionGroupActions.Create, ProjectPermissionSub.Groups);
|
||||
|
||||
let group: TGroups | null = null;
|
||||
if (isUuidV4(groupIdOrName)) {
|
||||
group = await groupDAL.findOne({ orgId: actorOrgId, id: groupIdOrName });
|
||||
}
|
||||
if (!group) {
|
||||
group = await groupDAL.findOne({ orgId: actorOrgId, name: groupIdOrName });
|
||||
}
|
||||
|
||||
if (!group) throw new NotFoundError({ message: `Failed to find group with ID or name ${groupIdOrName}` });
|
||||
|
||||
const existingGroup = await groupProjectDAL.findOne({ groupId: group.id, projectId: project.id });
|
||||
if (existingGroup)
|
||||
throw new BadRequestError({
|
||||
message: `Group with ID ${group.id} already exists in project with id ${project.id}`
|
||||
});
|
||||
|
||||
for await (const { role: requestedRoleChange } of roles) {
|
||||
const { permission: rolePermission } = await permissionService.getProjectPermissionByRole(
|
||||
requestedRoleChange,
|
||||
project.id
|
||||
);
|
||||
|
||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||
membership.shouldUseNewPrivilegeSystem,
|
||||
ProjectPermissionGroupActions.GrantPrivileges,
|
||||
ProjectPermissionSub.Groups,
|
||||
permission,
|
||||
rolePermission
|
||||
);
|
||||
if (!permissionBoundary.isValid)
|
||||
throw new PermissionBoundaryError({
|
||||
message: constructPermissionErrorMessage(
|
||||
"Failed to assign group to role",
|
||||
membership.shouldUseNewPrivilegeSystem,
|
||||
ProjectPermissionGroupActions.GrantPrivileges,
|
||||
ProjectPermissionSub.Groups
|
||||
),
|
||||
details: { missingPermissions: permissionBoundary.missingPermissions }
|
||||
});
|
||||
}
|
||||
|
||||
// validate custom roles input
|
||||
const customInputRoles = roles.filter(
|
||||
({ role }) => !Object.values(ProjectMembershipRole).includes(role as ProjectMembershipRole)
|
||||
);
|
||||
const hasCustomRole = Boolean(customInputRoles.length);
|
||||
const customRoles = hasCustomRole
|
||||
? await projectRoleDAL.find({
|
||||
projectId: project.id,
|
||||
$in: { slug: customInputRoles.map(({ role }) => role) }
|
||||
})
|
||||
: [];
|
||||
|
||||
if (customRoles.length !== customInputRoles.length) {
|
||||
const customRoleSlugs = customRoles.map((customRole) => customRole.slug);
|
||||
const missingInputRoles = customInputRoles
|
||||
.filter((inputRole) => !customRoleSlugs.includes(inputRole.role))
|
||||
.map((role) => role.role);
|
||||
|
||||
throw new NotFoundError({
|
||||
message: `Custom role/s not found: ${missingInputRoles.join(", ")}`
|
||||
});
|
||||
}
|
||||
const customRolesGroupBySlug = groupBy(customRoles, ({ slug }) => slug);
|
||||
|
||||
const projectGroup = await groupProjectDAL.transaction(async (tx) => {
|
||||
const groupProjectMembership = await groupProjectDAL.create(
|
||||
{
|
||||
groupId: group!.id,
|
||||
projectId: project.id
|
||||
},
|
||||
tx
|
||||
);
|
||||
|
||||
const sanitizedProjectMembershipRoles = roles.map((inputRole) => {
|
||||
const isCustomRole = Boolean(customRolesGroupBySlug?.[inputRole.role]?.[0]);
|
||||
if (!inputRole.isTemporary) {
|
||||
return {
|
||||
projectMembershipId: groupProjectMembership.id,
|
||||
role: isCustomRole ? ProjectMembershipRole.Custom : inputRole.role,
|
||||
customRoleId: customRolesGroupBySlug[inputRole.role] ? customRolesGroupBySlug[inputRole.role][0].id : null
|
||||
};
|
||||
}
|
||||
|
||||
// check cron or relative here later for now its just relative
|
||||
const relativeTimeInMs = ms(inputRole.temporaryRange);
|
||||
return {
|
||||
projectMembershipId: groupProjectMembership.id,
|
||||
role: isCustomRole ? ProjectMembershipRole.Custom : inputRole.role,
|
||||
customRoleId: customRolesGroupBySlug[inputRole.role] ? customRolesGroupBySlug[inputRole.role][0].id : null,
|
||||
isTemporary: true,
|
||||
temporaryMode: ProjectUserMembershipTemporaryMode.Relative,
|
||||
temporaryRange: inputRole.temporaryRange,
|
||||
temporaryAccessStartTime: new Date(inputRole.temporaryAccessStartTime),
|
||||
temporaryAccessEndTime: new Date(new Date(inputRole.temporaryAccessStartTime).getTime() + relativeTimeInMs)
|
||||
};
|
||||
});
|
||||
|
||||
await groupProjectMembershipRoleDAL.insertMany(sanitizedProjectMembershipRoles, tx);
|
||||
|
||||
// share project key with users in group that have not
|
||||
// individually been added to the project and that are not part of
|
||||
// other groups that are in the project
|
||||
const groupMembers = await userGroupMembershipDAL.findGroupMembersNotInProject(group!.id, project.id, tx);
|
||||
|
||||
if (groupMembers.length && (project.version === ProjectVersion.V1 || project.version === ProjectVersion.V2)) {
|
||||
const ghostUser = await projectDAL.findProjectGhostUser(project.id, tx);
|
||||
|
||||
if (!ghostUser) {
|
||||
throw new NotFoundError({
|
||||
message: `Failed to find project owner of project with name ${project.name}`
|
||||
});
|
||||
}
|
||||
|
||||
const ghostUserLatestKey = await projectKeyDAL.findLatestProjectKey(ghostUser.id, project.id, tx);
|
||||
|
||||
if (!ghostUserLatestKey) {
|
||||
throw new NotFoundError({
|
||||
message: `Failed to find project owner's latest key in project with name ${project.name}`
|
||||
});
|
||||
}
|
||||
|
||||
if (!ghostUserLatestKey.sender.publicKey) {
|
||||
throw new NotFoundError({
|
||||
message: `Failed to find project owner's latest key in project with name ${project.name}`
|
||||
});
|
||||
}
|
||||
|
||||
const bot = await projectBotDAL.findOne({ projectId: project.id }, tx);
|
||||
|
||||
if (!bot) {
|
||||
throw new NotFoundError({
|
||||
message: `Failed to find project bot in project with name ${project.name}`
|
||||
});
|
||||
}
|
||||
|
||||
const botPrivateKey = crypto
|
||||
.encryption()
|
||||
.symmetric()
|
||||
.decryptWithRootEncryptionKey({
|
||||
keyEncoding: bot.keyEncoding as SecretKeyEncoding,
|
||||
iv: bot.iv,
|
||||
tag: bot.tag,
|
||||
ciphertext: bot.encryptedPrivateKey
|
||||
});
|
||||
|
||||
const plaintextProjectKey = crypto.encryption().asymmetric().decrypt({
|
||||
ciphertext: ghostUserLatestKey.encryptedKey,
|
||||
nonce: ghostUserLatestKey.nonce,
|
||||
publicKey: ghostUserLatestKey.sender.publicKey,
|
||||
privateKey: botPrivateKey
|
||||
});
|
||||
|
||||
const projectKeyData = groupMembers.map(({ user: { publicKey, id } }) => {
|
||||
if (!publicKey) {
|
||||
throw new NotFoundError({
|
||||
message: `Failed to find user's public key in project with name ${project.name}`
|
||||
});
|
||||
}
|
||||
|
||||
const { ciphertext: encryptedKey, nonce } = crypto
|
||||
.encryption()
|
||||
.asymmetric()
|
||||
.encrypt(plaintextProjectKey, publicKey, botPrivateKey);
|
||||
|
||||
return {
|
||||
encryptedKey,
|
||||
nonce,
|
||||
senderId: ghostUser.id,
|
||||
receiverId: id,
|
||||
projectId: project.id
|
||||
};
|
||||
});
|
||||
|
||||
await projectKeyDAL.insertMany(projectKeyData, tx);
|
||||
}
|
||||
|
||||
return groupProjectMembership;
|
||||
});
|
||||
|
||||
await permissionService.invalidateProjectPermissionCache(projectId);
|
||||
|
||||
return projectGroup;
|
||||
};
|
||||
|
||||
const updateGroupInProject = async ({
|
||||
projectId,
|
||||
groupId,
|
||||
roles,
|
||||
actor,
|
||||
actorId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
}: TUpdateProjectGroupDTO) => {
|
||||
const project = await projectDAL.findById(projectId);
|
||||
|
||||
if (!project) throw new NotFoundError({ message: `Failed to find project with ID ${projectId}` });
|
||||
|
||||
const { permission, membership } = await permissionService.getProjectPermission({
|
||||
actor,
|
||||
actorId,
|
||||
projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId,
|
||||
actionProjectType: ActionProjectType.Any
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionGroupActions.Edit, ProjectPermissionSub.Groups);
|
||||
|
||||
const group = await groupDAL.findOne({ orgId: actorOrgId, id: groupId });
|
||||
if (!group) throw new NotFoundError({ message: `Failed to find group with ID ${groupId}` });
|
||||
|
||||
const projectGroup = await groupProjectDAL.findOne({ groupId: group.id, projectId: project.id });
|
||||
if (!projectGroup) throw new NotFoundError({ message: `Failed to find group with ID ${groupId}` });
|
||||
|
||||
for await (const { role: requestedRoleChange } of roles) {
|
||||
const { permission: rolePermission } = await permissionService.getProjectPermissionByRole(
|
||||
requestedRoleChange,
|
||||
project.id
|
||||
);
|
||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||
membership.shouldUseNewPrivilegeSystem,
|
||||
ProjectPermissionGroupActions.GrantPrivileges,
|
||||
ProjectPermissionSub.Groups,
|
||||
permission,
|
||||
rolePermission
|
||||
);
|
||||
if (!permissionBoundary.isValid)
|
||||
throw new PermissionBoundaryError({
|
||||
message: constructPermissionErrorMessage(
|
||||
"Failed to assign group to role",
|
||||
membership.shouldUseNewPrivilegeSystem,
|
||||
ProjectPermissionGroupActions.GrantPrivileges,
|
||||
ProjectPermissionSub.Groups
|
||||
),
|
||||
details: { missingPermissions: permissionBoundary.missingPermissions }
|
||||
});
|
||||
}
|
||||
|
||||
// validate custom roles input
|
||||
const customInputRoles = roles.filter(
|
||||
({ role }) => !Object.values(ProjectMembershipRole).includes(role as ProjectMembershipRole)
|
||||
);
|
||||
const hasCustomRole = Boolean(customInputRoles.length);
|
||||
const customRoles = hasCustomRole
|
||||
? await projectRoleDAL.find({
|
||||
projectId: project.id,
|
||||
$in: { slug: customInputRoles.map(({ role }) => role) }
|
||||
})
|
||||
: [];
|
||||
if (customRoles.length !== customInputRoles.length) {
|
||||
const customRoleSlugs = customRoles.map((customRole) => customRole.slug);
|
||||
const missingInputRoles = customInputRoles
|
||||
.filter((inputRole) => !customRoleSlugs.includes(inputRole.role))
|
||||
.map((role) => role.role);
|
||||
|
||||
throw new NotFoundError({
|
||||
message: `Custom role/s not found: ${missingInputRoles.join(", ")}`
|
||||
});
|
||||
}
|
||||
|
||||
const customRolesGroupBySlug = groupBy(customRoles, ({ slug }) => slug);
|
||||
|
||||
const sanitizedProjectMembershipRoles = roles.map((inputRole) => {
|
||||
const isCustomRole = Boolean(customRolesGroupBySlug?.[inputRole.role]?.[0]);
|
||||
if (!inputRole.isTemporary) {
|
||||
return {
|
||||
projectMembershipId: projectGroup.id,
|
||||
role: isCustomRole ? ProjectMembershipRole.Custom : inputRole.role,
|
||||
customRoleId: customRolesGroupBySlug[inputRole.role] ? customRolesGroupBySlug[inputRole.role][0].id : null
|
||||
};
|
||||
}
|
||||
|
||||
// check cron or relative here later for now its just relative
|
||||
const relativeTimeInMs = ms(inputRole.temporaryRange);
|
||||
return {
|
||||
projectMembershipId: projectGroup.id,
|
||||
role: isCustomRole ? ProjectMembershipRole.Custom : inputRole.role,
|
||||
customRoleId: customRolesGroupBySlug[inputRole.role] ? customRolesGroupBySlug[inputRole.role][0].id : null,
|
||||
isTemporary: true,
|
||||
temporaryMode: ProjectUserMembershipTemporaryMode.Relative,
|
||||
temporaryRange: inputRole.temporaryRange,
|
||||
temporaryAccessStartTime: new Date(inputRole.temporaryAccessStartTime),
|
||||
temporaryAccessEndTime: new Date(new Date(inputRole.temporaryAccessStartTime).getTime() + relativeTimeInMs)
|
||||
};
|
||||
});
|
||||
|
||||
const updatedRoles = await groupProjectMembershipRoleDAL.transaction(async (tx) => {
|
||||
await groupProjectMembershipRoleDAL.delete({ projectMembershipId: projectGroup.id }, tx);
|
||||
return groupProjectMembershipRoleDAL.insertMany(sanitizedProjectMembershipRoles, tx);
|
||||
});
|
||||
|
||||
await permissionService.invalidateProjectPermissionCache(projectId);
|
||||
|
||||
return updatedRoles;
|
||||
};
|
||||
|
||||
const removeGroupFromProject = async ({
|
||||
projectId,
|
||||
groupId,
|
||||
actorId,
|
||||
actor,
|
||||
actorOrgId,
|
||||
actorAuthMethod
|
||||
}: TDeleteProjectGroupDTO) => {
|
||||
const project = await projectDAL.findById(projectId);
|
||||
|
||||
if (!project) throw new NotFoundError({ message: `Failed to find project with ID ${projectId}` });
|
||||
|
||||
const group = await groupDAL.findOne({ orgId: actorOrgId, id: groupId });
|
||||
if (!group) throw new NotFoundError({ message: `Failed to find group with ID ${groupId}` });
|
||||
|
||||
const groupProjectMembership = await groupProjectDAL.findOne({ groupId: group.id, projectId: project.id });
|
||||
if (!groupProjectMembership) throw new NotFoundError({ message: `Failed to find group with ID ${groupId}` });
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actor,
|
||||
actorId,
|
||||
projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId,
|
||||
actionProjectType: ActionProjectType.Any
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionGroupActions.Delete, ProjectPermissionSub.Groups);
|
||||
|
||||
const deletedProjectGroup = await groupProjectDAL.transaction(async (tx) => {
|
||||
const groupMembersNotInProject = await userGroupMembershipDAL.findGroupMembersNotInProject(
|
||||
group.id,
|
||||
project.id,
|
||||
tx
|
||||
);
|
||||
|
||||
if (groupMembersNotInProject.length) {
|
||||
await projectKeyDAL.delete(
|
||||
{
|
||||
projectId: project.id,
|
||||
$in: {
|
||||
receiverId: groupMembersNotInProject.map(({ user: { id } }) => id)
|
||||
}
|
||||
},
|
||||
tx
|
||||
);
|
||||
}
|
||||
|
||||
const [projectGroup] = await groupProjectDAL.delete({ groupId: group.id, projectId: project.id }, tx);
|
||||
return projectGroup;
|
||||
});
|
||||
|
||||
await permissionService.invalidateProjectPermissionCache(projectId);
|
||||
|
||||
return deletedProjectGroup;
|
||||
};
|
||||
|
||||
const listGroupsInProject = async ({
|
||||
projectId,
|
||||
actor,
|
||||
actorId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
}: TListProjectGroupDTO) => {
|
||||
const project = await projectDAL.findById(projectId);
|
||||
|
||||
if (!project) {
|
||||
throw new NotFoundError({ message: `Failed to find project with ID ${projectId}` });
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actor,
|
||||
actorId,
|
||||
projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId,
|
||||
actionProjectType: ActionProjectType.Any
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionGroupActions.Read, ProjectPermissionSub.Groups);
|
||||
|
||||
const groupMemberships = await groupProjectDAL.findByProjectId(project.id);
|
||||
return groupMemberships;
|
||||
};
|
||||
|
||||
const getGroupInProject = async ({
|
||||
actor,
|
||||
actorId,
|
||||
actorAuthMethod,
|
||||
actorOrgId,
|
||||
groupId,
|
||||
projectId
|
||||
}: TGetGroupInProjectDTO) => {
|
||||
const project = await projectDAL.findById(projectId);
|
||||
|
||||
if (!project) {
|
||||
throw new NotFoundError({ message: `Failed to find project with ID ${projectId}` });
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actor,
|
||||
actorId,
|
||||
projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId,
|
||||
actionProjectType: ActionProjectType.Any
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionGroupActions.Read, ProjectPermissionSub.Groups);
|
||||
|
||||
const [groupMembership] = await groupProjectDAL.findByProjectId(project.id, {
|
||||
groupId
|
||||
});
|
||||
|
||||
if (!groupMembership) {
|
||||
throw new NotFoundError({
|
||||
message: `Group membership with ID ${groupId} not found in project with ID ${projectId}`
|
||||
});
|
||||
}
|
||||
|
||||
return groupMembership;
|
||||
};
|
||||
|
||||
const listProjectGroupUsers = async ({
|
||||
id,
|
||||
projectId,
|
||||
@@ -542,11 +65,6 @@ export const groupProjectServiceFactory = ({
|
||||
};
|
||||
|
||||
return {
|
||||
addGroupToProject,
|
||||
updateGroupInProject,
|
||||
removeGroupFromProject,
|
||||
listGroupsInProject,
|
||||
getGroupInProject,
|
||||
listProjectGroupUsers
|
||||
};
|
||||
};
|
||||
|
||||
@@ -217,8 +217,8 @@ export const membershipGroupServiceFactory = ({
|
||||
},
|
||||
tx
|
||||
);
|
||||
await membershipRoleDAL.insertMany(roleDocs, tx);
|
||||
return doc;
|
||||
const roles = await membershipRoleDAL.insertMany(roleDocs, tx);
|
||||
return { ...doc, roles };
|
||||
});
|
||||
|
||||
return { membership: membershipDoc };
|
||||
@@ -276,7 +276,7 @@ export const membershipGroupServiceFactory = ({
|
||||
: undefined
|
||||
}
|
||||
});
|
||||
return memberships;
|
||||
return { memberships: memberships.data, totalCount: memberships.totalCount };
|
||||
};
|
||||
|
||||
const getMembershipByGroupId = async (dto: TGetMembershipGroupByGroupIdDTO) => {
|
||||
@@ -290,7 +290,7 @@ export const membershipGroupServiceFactory = ({
|
||||
});
|
||||
if (!membership) throw new NotFoundError({ message: `Group membership not found` });
|
||||
|
||||
return membership;
|
||||
return { membership };
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
@@ -54,14 +54,11 @@ export type TUpdateMembershipGroupDTO = {
|
||||
export type TListMembershipGroupDTO = {
|
||||
permission: OrgServiceActor;
|
||||
scopeData: AccessScopeData;
|
||||
selector: {
|
||||
groupId: string;
|
||||
};
|
||||
data: {
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
groupName?: string;
|
||||
roles: string[];
|
||||
roles?: string[];
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ type TProjectMembershipGroupScopeFactoryDep = {
|
||||
membershipGroupDAL: Pick<TMembershipGroupDALFactory, "findOne">;
|
||||
};
|
||||
|
||||
// TODO(simp): check for group in org
|
||||
export const newProjectMembershipGroupFactory = ({
|
||||
permissionService,
|
||||
orgDAL,
|
||||
|
||||
@@ -1,20 +1,22 @@
|
||||
import { AccessScope } from "@app/db/schemas";
|
||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||
import { TProjectUserAdditionalPrivilegeDALFactory } from "@app/ee/services/project-user-additional-privilege/project-user-additional-privilege-dal";
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
import { TOrgDALFactory } from "@app/services/org/org-dal";
|
||||
import { TProjectKeyDALFactory } from "@app/services/project-key/project-key-dal";
|
||||
import { TProjectMembershipDALFactory } from "@app/services/project-membership/project-membership-dal";
|
||||
import { TUserAliasDALFactory } from "@app/services/user-alias/user-alias-dal";
|
||||
|
||||
import { TMembershipUserDALFactory } from "../membership-user/membership-user-dal";
|
||||
|
||||
type TDeleteOrgMembership = {
|
||||
orgMembershipId: string;
|
||||
orgId: string;
|
||||
orgDAL: Pick<TOrgDALFactory, "findMembership" | "deleteMembershipById" | "transaction">;
|
||||
projectMembershipDAL: Pick<TProjectMembershipDALFactory, "delete" | "findProjectMembershipsByUserId">;
|
||||
projectMembershipDAL: Pick<TProjectMembershipDALFactory, "findProjectMembershipsByUserId">;
|
||||
membershipUserDAL: Pick<TMembershipUserDALFactory, "delete">;
|
||||
projectKeyDAL: Pick<TProjectKeyDALFactory, "find" | "delete">;
|
||||
userAliasDAL: Pick<TUserAliasDALFactory, "delete">;
|
||||
licenseService: Pick<TLicenseServiceFactory, "updateSubscriptionOrgMemberCount">;
|
||||
projectUserAdditionalPrivilegeDAL: Pick<TProjectUserAdditionalPrivilegeDALFactory, "delete">;
|
||||
userId?: string;
|
||||
};
|
||||
|
||||
@@ -22,11 +24,11 @@ type TDeleteOrgMemberships = {
|
||||
orgMembershipIds: string[];
|
||||
orgId: string;
|
||||
orgDAL: Pick<TOrgDALFactory, "findMembership" | "deleteMembershipsById" | "transaction">;
|
||||
projectMembershipDAL: Pick<TProjectMembershipDALFactory, "delete" | "findProjectMembershipsByUserIds">;
|
||||
projectMembershipDAL: Pick<TProjectMembershipDALFactory, "findProjectMembershipsByUserIds">;
|
||||
membershipUserDAL: Pick<TMembershipUserDALFactory, "delete">;
|
||||
projectKeyDAL: Pick<TProjectKeyDALFactory, "find" | "delete">;
|
||||
userAliasDAL: Pick<TUserAliasDALFactory, "delete">;
|
||||
licenseService: Pick<TLicenseServiceFactory, "updateSubscriptionOrgMemberCount">;
|
||||
projectUserAdditionalPrivilegeDAL: Pick<TProjectUserAdditionalPrivilegeDALFactory, "delete">;
|
||||
userId?: string;
|
||||
};
|
||||
|
||||
@@ -35,46 +37,41 @@ export const deleteOrgMembershipFn = async ({
|
||||
orgId,
|
||||
orgDAL,
|
||||
projectMembershipDAL,
|
||||
projectUserAdditionalPrivilegeDAL,
|
||||
projectKeyDAL,
|
||||
userAliasDAL,
|
||||
licenseService,
|
||||
userId
|
||||
userId,
|
||||
membershipUserDAL
|
||||
}: TDeleteOrgMembership) => {
|
||||
const deletedMembership = await orgDAL.transaction(async (tx) => {
|
||||
const orgMembership = await orgDAL.deleteMembershipById(orgMembershipId, orgId, tx);
|
||||
|
||||
if (userId && orgMembership.userId === userId) {
|
||||
if (userId && orgMembership.actorUserId === userId) {
|
||||
// scott: this is temporary, we will add a leave org endpoint with proper handling to ensure org isn't abandoned/broken
|
||||
throw new BadRequestError({ message: "You cannot remove yourself from an organization" });
|
||||
}
|
||||
|
||||
if (!orgMembership.userId) {
|
||||
const deletedUserId = orgMembership.actorUserId;
|
||||
if (!deletedUserId) {
|
||||
await licenseService.updateSubscriptionOrgMemberCount(orgId);
|
||||
return orgMembership;
|
||||
}
|
||||
|
||||
await userAliasDAL.delete(
|
||||
{
|
||||
userId: orgMembership.userId,
|
||||
userId: deletedUserId,
|
||||
orgId
|
||||
},
|
||||
tx
|
||||
);
|
||||
|
||||
await projectUserAdditionalPrivilegeDAL.delete(
|
||||
{
|
||||
userId: orgMembership.userId
|
||||
},
|
||||
tx
|
||||
);
|
||||
|
||||
// Get all the project memberships of the user in the organization
|
||||
const projectMemberships = await projectMembershipDAL.findProjectMembershipsByUserId(orgId, orgMembership.userId);
|
||||
const projectMemberships = await projectMembershipDAL.findProjectMembershipsByUserId(orgId, deletedUserId);
|
||||
|
||||
// Delete all the project memberships of the user in the organization
|
||||
await projectMembershipDAL.delete(
|
||||
await membershipUserDAL.delete(
|
||||
{
|
||||
scope: AccessScope.Project,
|
||||
$in: {
|
||||
id: projectMemberships.map((membership) => membership.id)
|
||||
}
|
||||
@@ -87,7 +84,7 @@ export const deleteOrgMembershipFn = async ({
|
||||
$in: {
|
||||
projectId: projectMemberships.map((membership) => membership.projectId)
|
||||
},
|
||||
receiverId: orgMembership.userId
|
||||
receiverId: deletedUserId
|
||||
});
|
||||
|
||||
// Delete all the project keys of the user in the organization
|
||||
@@ -112,18 +109,18 @@ export const deleteOrgMembershipsFn = async ({
|
||||
orgId,
|
||||
orgDAL,
|
||||
projectMembershipDAL,
|
||||
projectUserAdditionalPrivilegeDAL,
|
||||
projectKeyDAL,
|
||||
userAliasDAL,
|
||||
licenseService,
|
||||
userId
|
||||
userId,
|
||||
membershipUserDAL
|
||||
}: TDeleteOrgMemberships) => {
|
||||
const deletedMemberships = await orgDAL.transaction(async (tx) => {
|
||||
const orgMemberships = await orgDAL.deleteMembershipsById(orgMembershipIds, orgId, tx);
|
||||
|
||||
const membershipUserIds = orgMemberships
|
||||
.filter((member) => Boolean(member.userId))
|
||||
.map((member) => member.userId) as string[];
|
||||
.filter((member) => Boolean(member.actorUserId))
|
||||
.map((member) => member.actorUserId) as string[];
|
||||
|
||||
if (userId && membershipUserIds.includes(userId)) {
|
||||
// scott: this is temporary, we will add a leave org endpoint with proper handling to ensure org isn't abandoned/broken
|
||||
@@ -145,21 +142,13 @@ export const deleteOrgMembershipsFn = async ({
|
||||
tx
|
||||
);
|
||||
|
||||
await projectUserAdditionalPrivilegeDAL.delete(
|
||||
{
|
||||
$in: {
|
||||
userId: membershipUserIds
|
||||
}
|
||||
},
|
||||
tx
|
||||
);
|
||||
|
||||
// Get all the project memberships of the users in the organization
|
||||
const projectMemberships = await projectMembershipDAL.findProjectMembershipsByUserIds(orgId, membershipUserIds);
|
||||
|
||||
// Delete all the project memberships of the users in the organization
|
||||
await projectMembershipDAL.delete(
|
||||
await membershipUserDAL.delete(
|
||||
{
|
||||
scope: AccessScope.Project,
|
||||
$in: {
|
||||
id: projectMemberships.map((membership) => membership.id)
|
||||
}
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
import { TDbClient } from "@app/db";
|
||||
import { TableName } from "@app/db/schemas";
|
||||
import { ormify } from "@app/lib/knex";
|
||||
|
||||
export type TProjectUserMembershipRoleDALFactory = ReturnType<typeof projectUserMembershipRoleDALFactory>;
|
||||
|
||||
export const projectUserMembershipRoleDALFactory = (db: TDbClient) => {
|
||||
const orm = ormify(db, TableName.ProjectUserMembershipRole);
|
||||
return orm;
|
||||
};
|
||||
Reference in New Issue
Block a user