feat: complete project identities

This commit is contained in:
Scott Wilson
2025-11-13 12:52:38 +05:30
committed by =
parent 1fe635678e
commit b32544e76c
111 changed files with 4820 additions and 2113 deletions
@@ -0,0 +1,24 @@
import { TKeyStoreFactory } from "@app/keystore/keystore";
export const getIdentityActiveLockoutAuthMethods = async (
identityId: string,
keyStore: Pick<TKeyStoreFactory, "getKeysByPattern" | "getItem">
) => {
const activeLockouts = await keyStore.getKeysByPattern(`lockout:identity:${identityId}:*`);
const activeLockoutAuthMethods = new Set<string>();
for await (const key of activeLockouts) {
const parts = key.split(":");
if (parts.length > 3) {
const lockoutRaw = await keyStore.getItem(key);
if (lockoutRaw) {
const lockout = JSON.parse(lockoutRaw) as { lockedOut: boolean };
if (lockout.lockedOut) {
activeLockoutAuthMethods.add(parts[3]);
}
}
}
}
return Array.from(activeLockoutAuthMethods);
};
@@ -1,7 +1,9 @@
import { AccessScope, OrgMembershipRole } from "@app/db/schemas";
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
import { TKeyStoreFactory } from "@app/keystore/keystore";
import { BadRequestError, NotFoundError } from "@app/lib/errors";
import { getIdentityActiveLockoutAuthMethods } from "@app/services/identity-v2/identity-fns";
import { TIdentityMetadataDALFactory } from "../identity/identity-metadata-dal";
import { TMembershipRoleDALFactory } from "../membership/membership-role-dal";
@@ -24,6 +26,7 @@ type TScopedIdentityV2ServiceFactoryDep = {
membershipIdentityDAL: TMembershipIdentityDALFactory;
membershipRoleDAL: TMembershipRoleDALFactory;
identityMetadataDAL: TIdentityMetadataDALFactory;
keyStore: Pick<TKeyStoreFactory, "getKeysByPattern" | "getItem">;
};
export type TScopedIdentityV2ServiceFactory = ReturnType<typeof identityV2ServiceFactory>;
@@ -34,7 +37,8 @@ export const identityV2ServiceFactory = ({
licenseService,
membershipIdentityDAL,
membershipRoleDAL,
identityMetadataDAL
identityMetadataDAL,
keyStore
}: TScopedIdentityV2ServiceFactoryDep) => {
const orgFactory = newOrgIdentityFactory({
permissionService
@@ -217,7 +221,9 @@ export const identityV2ServiceFactory = ({
const identity = await identityDAL.getIdentityById(dto.scopeData, dto.selector.identityId);
if (!identity) throw new NotFoundError({ message: `Identity with id ${dto.selector.identityId} not found` });
return { identity };
const activeLockoutAuthMethods = await getIdentityActiveLockoutAuthMethods(identity.id, keyStore);
return { identity: { ...identity, activeLockoutAuthMethods } };
};
const listIdentities = async (dto: TListIdentityV2DTO) => {
@@ -1,4 +1,4 @@
import { ForbiddenError } from "@casl/ability";
import { ForbiddenError, subject } from "@casl/ability";
import { AccessScope, ActionProjectType } from "@app/db/schemas";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
@@ -47,7 +47,7 @@ export const newProjectIdentityFactory = ({ permissionService }: TProjectIdentit
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionIdentityActions.Edit,
ProjectPermissionSub.Identity
subject(ProjectPermissionSub.Identity, { identityId: dto.selector.identityId })
);
};
@@ -63,7 +63,7 @@ export const newProjectIdentityFactory = ({ permissionService }: TProjectIdentit
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionIdentityActions.Delete,
ProjectPermissionSub.Identity
subject(ProjectPermissionSub.Identity, { identityId: dto.selector.identityId })
);
};
@@ -95,7 +95,7 @@ export const newProjectIdentityFactory = ({ permissionService }: TProjectIdentit
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionIdentityActions.Read,
ProjectPermissionSub.Identity
subject(ProjectPermissionSub.Identity, { identityId: dto.selector.identityId })
);
};