mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-14 07:30:20 +00:00
Merge pull request #4270 from Infisical/daniel/srp-removal-round-2
feat: srp removal
This commit is contained in:
@@ -1,8 +1,16 @@
|
||||
import { TUsers } from "@app/db/schemas";
|
||||
import { isAuthMethodSaml } from "@app/ee/services/permission/permission-fns";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { request } from "@app/lib/config/request";
|
||||
import { crypto } from "@app/lib/crypto";
|
||||
import { ForbiddenRequestError, UnauthorizedError } from "@app/lib/errors";
|
||||
import { BadRequestError, ForbiddenRequestError, UnauthorizedError } from "@app/lib/errors";
|
||||
|
||||
import { AuthModeProviderJwtTokenPayload, AuthModeProviderSignUpTokenPayload, AuthTokenType } from "./auth-type";
|
||||
import {
|
||||
AuthMethod,
|
||||
AuthModeProviderJwtTokenPayload,
|
||||
AuthModeProviderSignUpTokenPayload,
|
||||
AuthTokenType
|
||||
} from "./auth-type";
|
||||
|
||||
export const validateProviderAuthToken = (providerToken: string, username?: string) => {
|
||||
if (!providerToken) throw new UnauthorizedError();
|
||||
@@ -97,3 +105,50 @@ export const enforceUserLockStatus = (isLocked: boolean, temporaryLockDateEnd?:
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const verifyCaptcha = async (user: TUsers, captchaToken?: string) => {
|
||||
const appCfg = getConfig();
|
||||
if (
|
||||
user.consecutiveFailedPasswordAttempts &&
|
||||
user.consecutiveFailedPasswordAttempts >= 10 &&
|
||||
Boolean(appCfg.CAPTCHA_SECRET)
|
||||
) {
|
||||
if (!captchaToken) {
|
||||
throw new BadRequestError({
|
||||
name: "Captcha Required",
|
||||
message: "Accomplish the required captcha by logging in via Web"
|
||||
});
|
||||
}
|
||||
|
||||
// validate captcha token
|
||||
const response = await request.postForm<{ success: boolean }>("https://api.hcaptcha.com/siteverify", {
|
||||
response: captchaToken,
|
||||
secret: appCfg.CAPTCHA_SECRET
|
||||
});
|
||||
|
||||
if (!response.data.success) {
|
||||
throw new BadRequestError({
|
||||
name: "Invalid Captcha"
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const getAuthMethodAndOrgId = (email: string, providerAuthToken?: string) => {
|
||||
let authMethod = AuthMethod.EMAIL;
|
||||
let organizationId: string | undefined;
|
||||
|
||||
if (providerAuthToken) {
|
||||
const decodedProviderToken = validateProviderAuthToken(providerAuthToken, email);
|
||||
|
||||
authMethod = decodedProviderToken.authMethod;
|
||||
if (
|
||||
(isAuthMethodSaml(authMethod) || [AuthMethod.LDAP, AuthMethod.OIDC].includes(authMethod)) &&
|
||||
decodedProviderToken.orgId
|
||||
) {
|
||||
organizationId = decodedProviderToken.orgId;
|
||||
}
|
||||
}
|
||||
|
||||
return { authMethod, organizationId };
|
||||
};
|
||||
|
||||
@@ -4,7 +4,6 @@ import { OrgMembershipRole, OrgMembershipStatus, TableName, TUsers, UserDeviceSc
|
||||
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";
|
||||
import { request } from "@app/lib/config/request";
|
||||
import { crypto, generateSrpServerKey, srpCheckClientProof } from "@app/lib/crypto";
|
||||
import { getUserPrivateKey } from "@app/lib/crypto/srp";
|
||||
import { BadRequestError, DatabaseError, ForbiddenRequestError, UnauthorizedError } from "@app/lib/errors";
|
||||
@@ -22,7 +21,8 @@ import { SmtpTemplates, TSmtpService } from "../smtp/smtp-service";
|
||||
import { LoginMethod } from "../super-admin/super-admin-types";
|
||||
import { TTotpServiceFactory } from "../totp/totp-service";
|
||||
import { TUserDALFactory } from "../user/user-dal";
|
||||
import { enforceUserLockStatus, validateProviderAuthToken } from "./auth-fns";
|
||||
import { UserEncryption } from "../user/user-types";
|
||||
import { enforceUserLockStatus, getAuthMethodAndOrgId, validateProviderAuthToken, verifyCaptcha } from "./auth-fns";
|
||||
import {
|
||||
TLoginClientProofDTO,
|
||||
TLoginGenServerPublicKeyDTO,
|
||||
@@ -208,6 +208,10 @@ export const authLoginServiceFactory = ({
|
||||
throw new Error("Failed to find user");
|
||||
}
|
||||
|
||||
if (!userEnc.salt || !userEnc.verifier) {
|
||||
throw new BadRequestError({ message: "Salt or verifier not found" });
|
||||
}
|
||||
|
||||
if (
|
||||
serverCfg.enabledLoginMethods &&
|
||||
!serverCfg.enabledLoginMethods.includes(LoginMethod.EMAIL) &&
|
||||
@@ -247,8 +251,6 @@ export const authLoginServiceFactory = ({
|
||||
captchaToken,
|
||||
password
|
||||
}: TLoginClientProofDTO) => {
|
||||
const appCfg = getConfig();
|
||||
|
||||
// akhilmhdh: case sensitive email resolution
|
||||
const usersByUsername = await userDAL.findUserEncKeyByUsername({
|
||||
username: email
|
||||
@@ -259,44 +261,11 @@ export const authLoginServiceFactory = ({
|
||||
const user = await userDAL.findById(userEnc.userId);
|
||||
const cfg = getConfig();
|
||||
|
||||
let authMethod = AuthMethod.EMAIL;
|
||||
let organizationId: string | undefined;
|
||||
const { authMethod, organizationId } = getAuthMethodAndOrgId(email, providerAuthToken);
|
||||
await verifyCaptcha(user, captchaToken);
|
||||
|
||||
if (providerAuthToken) {
|
||||
const decodedProviderToken = validateProviderAuthToken(providerAuthToken, email);
|
||||
|
||||
authMethod = decodedProviderToken.authMethod;
|
||||
if (
|
||||
(isAuthMethodSaml(authMethod) || [AuthMethod.LDAP, AuthMethod.OIDC].includes(authMethod)) &&
|
||||
decodedProviderToken.orgId
|
||||
) {
|
||||
organizationId = decodedProviderToken.orgId;
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
user.consecutiveFailedPasswordAttempts &&
|
||||
user.consecutiveFailedPasswordAttempts >= 10 &&
|
||||
Boolean(appCfg.CAPTCHA_SECRET)
|
||||
) {
|
||||
if (!captchaToken) {
|
||||
throw new BadRequestError({
|
||||
name: "Captcha Required",
|
||||
message: "Accomplish the required captcha by logging in via Web"
|
||||
});
|
||||
}
|
||||
|
||||
// validate captcha token
|
||||
const response = await request.postForm<{ success: boolean }>("https://api.hcaptcha.com/siteverify", {
|
||||
response: captchaToken,
|
||||
secret: appCfg.CAPTCHA_SECRET
|
||||
});
|
||||
|
||||
if (!response.data.success) {
|
||||
throw new BadRequestError({
|
||||
name: "Invalid Captcha"
|
||||
});
|
||||
}
|
||||
if (!userEnc.salt || !userEnc.verifier) {
|
||||
throw new BadRequestError({ message: "Salt or verifier not found" });
|
||||
}
|
||||
|
||||
if (!userEnc.serverPrivateKey || !userEnc.clientPublicKey) throw new Error("Failed to authenticate. Try again?");
|
||||
@@ -371,6 +340,80 @@ export const authLoginServiceFactory = ({
|
||||
return { token, user: userEnc } as const;
|
||||
};
|
||||
|
||||
const login = async ({
|
||||
email,
|
||||
password,
|
||||
ip,
|
||||
userAgent,
|
||||
providerAuthToken,
|
||||
captchaToken
|
||||
}: {
|
||||
email: string;
|
||||
password: string;
|
||||
ip: string;
|
||||
userAgent: string;
|
||||
providerAuthToken?: string;
|
||||
captchaToken?: string;
|
||||
}) => {
|
||||
const usersByUsername = await userDAL.findUserEncKeyByUsername({
|
||||
username: email
|
||||
});
|
||||
const userEnc =
|
||||
usersByUsername?.length > 1 ? usersByUsername.find((el) => el.username === email) : usersByUsername?.[0];
|
||||
|
||||
if (!userEnc) throw new BadRequestError({ message: "User not found" });
|
||||
|
||||
if (userEnc.encryptionVersion !== UserEncryption.V2) {
|
||||
throw new BadRequestError({ message: "Legacy encryption scheme not supported", name: "LegacyEncryptionScheme" });
|
||||
}
|
||||
|
||||
if (!userEnc.hashedPassword) {
|
||||
if (userEnc.authMethods?.includes(AuthMethod.EMAIL)) {
|
||||
throw new BadRequestError({
|
||||
message: "Legacy encryption scheme not supported",
|
||||
name: "LegacyEncryptionScheme"
|
||||
});
|
||||
}
|
||||
|
||||
throw new BadRequestError({ message: "No password found" });
|
||||
}
|
||||
|
||||
const { authMethod, organizationId } = getAuthMethodAndOrgId(email, providerAuthToken);
|
||||
await verifyCaptcha(userEnc, captchaToken);
|
||||
|
||||
if (!(await crypto.hashing().compareHash(password, userEnc.hashedPassword))) {
|
||||
await userDAL.update(
|
||||
{ id: userEnc.userId },
|
||||
{
|
||||
$incr: {
|
||||
consecutiveFailedPasswordAttempts: 1
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
throw new BadRequestError({ message: "Invalid username or email" });
|
||||
}
|
||||
|
||||
const token = await generateUserTokens({
|
||||
user: {
|
||||
...userEnc,
|
||||
id: userEnc.userId
|
||||
},
|
||||
ip,
|
||||
userAgent,
|
||||
authMethod,
|
||||
organizationId
|
||||
});
|
||||
|
||||
return {
|
||||
tokens: {
|
||||
accessToken: token.access,
|
||||
refreshToken: token.refresh
|
||||
},
|
||||
user: userEnc
|
||||
} as const;
|
||||
};
|
||||
|
||||
const selectOrganization = async ({
|
||||
userAgent,
|
||||
authJwtToken,
|
||||
@@ -862,6 +905,7 @@ export const authLoginServiceFactory = ({
|
||||
resendMfaToken,
|
||||
verifyMfaToken,
|
||||
selectOrganization,
|
||||
generateUserTokens
|
||||
generateUserTokens,
|
||||
login
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import { SecretEncryptionAlgo, SecretKeyEncoding } from "@app/db/schemas";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { generateSrpServerKey, srpCheckClientProof } from "@app/lib/crypto";
|
||||
import { crypto } from "@app/lib/crypto/cryptography";
|
||||
import { generateUserSrpKeys } from "@app/lib/crypto/srp";
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
import { logger } from "@app/lib/logger";
|
||||
import { OrgServiceActor } from "@app/lib/types";
|
||||
@@ -16,8 +13,6 @@ import { UserEncryption } from "../user/user-types";
|
||||
import { TAuthDALFactory } from "./auth-dal";
|
||||
import {
|
||||
ResetPasswordV2Type,
|
||||
TChangePasswordDTO,
|
||||
TCreateBackupPrivateKeyDTO,
|
||||
TResetPasswordV2DTO,
|
||||
TResetPasswordViaBackupKeyDTO,
|
||||
TSetupPasswordViaBackupKeyDTO
|
||||
@@ -40,79 +35,6 @@ export const authPaswordServiceFactory = ({
|
||||
smtpService,
|
||||
totpConfigDAL
|
||||
}: TAuthPasswordServiceFactoryDep) => {
|
||||
/*
|
||||
* Pre setup for pass change with srp protocol
|
||||
* Gets srp server user salt and server public key
|
||||
*/
|
||||
const generateServerPubKey = async (userId: string, clientPublicKey: string) => {
|
||||
const userEnc = await userDAL.findUserEncKeyByUserId(userId);
|
||||
if (!userEnc) throw new Error("Failed to find user");
|
||||
|
||||
const serverSrpKey = await generateSrpServerKey(userEnc.salt, userEnc.verifier);
|
||||
const userEncKeys = await userDAL.updateUserEncryptionByUserId(userEnc.userId, {
|
||||
clientPublicKey,
|
||||
serverPrivateKey: serverSrpKey.privateKey
|
||||
});
|
||||
if (!userEncKeys) throw new Error("Failed to update encryption key");
|
||||
return { salt: userEncKeys.salt, serverPublicKey: serverSrpKey.pubKey };
|
||||
};
|
||||
|
||||
/*
|
||||
* Change password to new pass
|
||||
* */
|
||||
const changePassword = async ({
|
||||
userId,
|
||||
clientProof,
|
||||
protectedKey,
|
||||
protectedKeyIV,
|
||||
protectedKeyTag,
|
||||
encryptedPrivateKey,
|
||||
encryptedPrivateKeyIV,
|
||||
encryptedPrivateKeyTag,
|
||||
salt,
|
||||
verifier,
|
||||
tokenVersionId,
|
||||
password
|
||||
}: TChangePasswordDTO) => {
|
||||
const userEnc = await userDAL.findUserEncKeyByUserId(userId);
|
||||
if (!userEnc) throw new Error("Failed to find user");
|
||||
|
||||
await userDAL.updateUserEncryptionByUserId(userEnc.userId, {
|
||||
serverPrivateKey: null,
|
||||
clientPublicKey: null
|
||||
});
|
||||
if (!userEnc.serverPrivateKey || !userEnc.clientPublicKey) throw new Error("Failed to authenticate. Try again?");
|
||||
const isValidClientProof = await srpCheckClientProof(
|
||||
userEnc.salt,
|
||||
userEnc.verifier,
|
||||
userEnc.serverPrivateKey,
|
||||
userEnc.clientPublicKey,
|
||||
clientProof
|
||||
);
|
||||
if (!isValidClientProof) throw new Error("Failed to authenticate. Try again?");
|
||||
|
||||
const appCfg = getConfig();
|
||||
const hashedPassword = await crypto.hashing().createHash(password, appCfg.SALT_ROUNDS);
|
||||
await userDAL.updateUserEncryptionByUserId(userId, {
|
||||
encryptionVersion: 2,
|
||||
protectedKey,
|
||||
protectedKeyIV,
|
||||
protectedKeyTag,
|
||||
encryptedPrivateKey,
|
||||
iv: encryptedPrivateKeyIV,
|
||||
tag: encryptedPrivateKeyTag,
|
||||
salt,
|
||||
verifier,
|
||||
serverPrivateKey: null,
|
||||
clientPublicKey: null,
|
||||
hashedPassword
|
||||
});
|
||||
|
||||
if (tokenVersionId) {
|
||||
await tokenService.clearTokenSessionById(userEnc.userId, tokenVersionId);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Email password reset flow via email. Step 1 send email
|
||||
*/
|
||||
@@ -215,58 +137,17 @@ export const authPaswordServiceFactory = ({
|
||||
}
|
||||
}
|
||||
|
||||
const newHashedPassword = await crypto.hashing().createHash(newPassword, cfg.SALT_ROUNDS);
|
||||
|
||||
// we need to get the original private key first for v2
|
||||
let privateKey: string;
|
||||
if (
|
||||
user.serverEncryptedPrivateKey &&
|
||||
user.serverEncryptedPrivateKeyTag &&
|
||||
user.serverEncryptedPrivateKeyIV &&
|
||||
user.serverEncryptedPrivateKeyEncoding &&
|
||||
user.encryptionVersion === UserEncryption.V2
|
||||
) {
|
||||
privateKey = crypto
|
||||
.encryption()
|
||||
.symmetric()
|
||||
.decryptWithRootEncryptionKey({
|
||||
iv: user.serverEncryptedPrivateKeyIV,
|
||||
tag: user.serverEncryptedPrivateKeyTag,
|
||||
ciphertext: user.serverEncryptedPrivateKey,
|
||||
keyEncoding: user.serverEncryptedPrivateKeyEncoding as SecretKeyEncoding
|
||||
});
|
||||
} else {
|
||||
if (user.encryptionVersion !== UserEncryption.V2) {
|
||||
throw new BadRequestError({
|
||||
message: "Cannot reset password without current credentials or recovery method",
|
||||
name: "Reset password"
|
||||
});
|
||||
}
|
||||
|
||||
const encKeys = await generateUserSrpKeys(user.username, newPassword, {
|
||||
publicKey: user.publicKey,
|
||||
privateKey
|
||||
});
|
||||
|
||||
const { tag, iv, ciphertext, encoding } = crypto.encryption().symmetric().encryptWithRootEncryptionKey(privateKey);
|
||||
const newHashedPassword = await crypto.hashing().createHash(newPassword, cfg.SALT_ROUNDS);
|
||||
|
||||
await userDAL.updateUserEncryptionByUserId(userId, {
|
||||
hashedPassword: newHashedPassword,
|
||||
|
||||
// srp params
|
||||
salt: encKeys.salt,
|
||||
verifier: encKeys.verifier,
|
||||
|
||||
protectedKey: encKeys.protectedKey,
|
||||
protectedKeyIV: encKeys.protectedKeyIV,
|
||||
protectedKeyTag: encKeys.protectedKeyTag,
|
||||
encryptedPrivateKey: encKeys.encryptedPrivateKey,
|
||||
iv: encKeys.encryptedPrivateKeyIV,
|
||||
tag: encKeys.encryptedPrivateKeyTag,
|
||||
|
||||
serverEncryptedPrivateKey: ciphertext,
|
||||
serverEncryptedPrivateKeyIV: iv,
|
||||
serverEncryptedPrivateKeyTag: tag,
|
||||
serverEncryptedPrivateKeyEncoding: encoding
|
||||
hashedPassword: newHashedPassword
|
||||
});
|
||||
|
||||
await tokenService.revokeAllMySessions(userId);
|
||||
@@ -317,66 +198,6 @@ export const authPaswordServiceFactory = ({
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
* backup key creation to give user's their access back when lost their password
|
||||
* this also needs to do the generateServerPubKey function to be executed first
|
||||
* then only client proof can be verified
|
||||
* */
|
||||
const createBackupPrivateKey = async ({
|
||||
clientProof,
|
||||
encryptedPrivateKey,
|
||||
salt,
|
||||
verifier,
|
||||
iv,
|
||||
tag,
|
||||
userId
|
||||
}: TCreateBackupPrivateKeyDTO) => {
|
||||
const userEnc = await userDAL.findUserEncKeyByUserId(userId);
|
||||
if (!userEnc || (userEnc && !userEnc.isAccepted)) {
|
||||
throw new Error("Failed to find user");
|
||||
}
|
||||
|
||||
if (!userEnc.clientPublicKey || !userEnc.serverPrivateKey) throw new Error("failed to create backup key");
|
||||
const isValidClientProff = await srpCheckClientProof(
|
||||
userEnc.salt,
|
||||
userEnc.verifier,
|
||||
userEnc.serverPrivateKey,
|
||||
userEnc.clientPublicKey,
|
||||
clientProof
|
||||
);
|
||||
if (!isValidClientProff) throw new Error("failed to create backup key");
|
||||
const backup = await authDAL.transaction(async (tx) => {
|
||||
const backupKey = await authDAL.upsertBackupKey(
|
||||
userEnc.userId,
|
||||
{
|
||||
encryptedPrivateKey,
|
||||
iv,
|
||||
tag,
|
||||
salt,
|
||||
verifier,
|
||||
algorithm: SecretEncryptionAlgo.AES_256_GCM,
|
||||
keyEncoding: SecretKeyEncoding.UTF8
|
||||
},
|
||||
tx
|
||||
);
|
||||
|
||||
await userDAL.updateUserEncryptionByUserId(
|
||||
userEnc.userId,
|
||||
{
|
||||
serverPrivateKey: null,
|
||||
clientPublicKey: null
|
||||
},
|
||||
tx
|
||||
);
|
||||
return backupKey;
|
||||
});
|
||||
|
||||
return backup;
|
||||
};
|
||||
|
||||
/*
|
||||
* Return user back up
|
||||
* */
|
||||
const getBackupPrivateKeyOfUser = async (userId: string) => {
|
||||
const user = await userDAL.findUserEncKeyByUserId(userId);
|
||||
if (!user || (user && !user.isAccepted)) {
|
||||
@@ -420,21 +241,7 @@ export const authPaswordServiceFactory = ({
|
||||
});
|
||||
};
|
||||
|
||||
const setupPassword = async (
|
||||
{
|
||||
encryptedPrivateKey,
|
||||
protectedKeyTag,
|
||||
protectedKey,
|
||||
protectedKeyIV,
|
||||
salt,
|
||||
verifier,
|
||||
encryptedPrivateKeyIV,
|
||||
encryptedPrivateKeyTag,
|
||||
password,
|
||||
token
|
||||
}: TSetupPasswordViaBackupKeyDTO,
|
||||
actor: OrgServiceActor
|
||||
) => {
|
||||
const setupPassword = async ({ password, token }: TSetupPasswordViaBackupKeyDTO, actor: OrgServiceActor) => {
|
||||
try {
|
||||
await tokenService.validateTokenForUser({
|
||||
type: TokenType.TOKEN_EMAIL_PASSWORD_SETUP,
|
||||
@@ -470,15 +277,7 @@ export const authPaswordServiceFactory = ({
|
||||
await userDAL.updateUserEncryptionByUserId(
|
||||
actor.id,
|
||||
{
|
||||
encryptionVersion: 2,
|
||||
protectedKey,
|
||||
protectedKeyIV,
|
||||
protectedKeyTag,
|
||||
encryptedPrivateKey,
|
||||
iv: encryptedPrivateKeyIV,
|
||||
tag: encryptedPrivateKeyTag,
|
||||
salt,
|
||||
verifier,
|
||||
encryptionVersion: UserEncryption.V2,
|
||||
hashedPassword,
|
||||
serverPrivateKey: null,
|
||||
clientPublicKey: null
|
||||
@@ -491,12 +290,9 @@ export const authPaswordServiceFactory = ({
|
||||
};
|
||||
|
||||
return {
|
||||
generateServerPubKey,
|
||||
changePassword,
|
||||
resetPasswordByBackupKey,
|
||||
sendPasswordResetEmail,
|
||||
verifyPasswordResetEmail,
|
||||
createBackupPrivateKey,
|
||||
getBackupPrivateKeyOfUser,
|
||||
sendPasswordSetupEmail,
|
||||
setupPassword,
|
||||
|
||||
@@ -1,18 +1,3 @@
|
||||
export type TChangePasswordDTO = {
|
||||
userId: string;
|
||||
clientProof: string;
|
||||
protectedKey: string;
|
||||
protectedKeyIV: string;
|
||||
protectedKeyTag: string;
|
||||
encryptedPrivateKey: string;
|
||||
encryptedPrivateKeyIV: string;
|
||||
encryptedPrivateKeyTag: string;
|
||||
salt: string;
|
||||
verifier: string;
|
||||
tokenVersionId?: string;
|
||||
password: string;
|
||||
};
|
||||
|
||||
export enum ResetPasswordV2Type {
|
||||
Recovery = "recovery",
|
||||
LoggedInReset = "logged-in-reset"
|
||||
@@ -39,14 +24,6 @@ export type TResetPasswordViaBackupKeyDTO = {
|
||||
};
|
||||
|
||||
export type TSetupPasswordViaBackupKeyDTO = {
|
||||
protectedKey: string;
|
||||
protectedKeyIV: string;
|
||||
protectedKeyTag: string;
|
||||
encryptedPrivateKey: string;
|
||||
encryptedPrivateKeyIV: string;
|
||||
encryptedPrivateKeyTag: string;
|
||||
salt: string;
|
||||
verifier: string;
|
||||
password: string;
|
||||
token: string;
|
||||
};
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { OrgMembershipStatus, SecretKeyEncoding, TableName } from "@app/db/schemas";
|
||||
import { 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";
|
||||
import { isAuthMethodSaml } from "@app/ee/services/permission/permission-fns";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { crypto } from "@app/lib/crypto/cryptography";
|
||||
import { generateUserSrpKeys, getUserPrivateKey } from "@app/lib/crypto/srp";
|
||||
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
||||
import { getMinExpiresIn } from "@app/lib/fn";
|
||||
import { isDisposableEmail } from "@app/lib/validator";
|
||||
@@ -41,7 +40,7 @@ type TAuthSignupDep = {
|
||||
| "findUserGroupMembershipsInProject"
|
||||
>;
|
||||
projectKeyDAL: Pick<TProjectKeyDALFactory, "find" | "findLatestProjectKey" | "insertMany">;
|
||||
projectDAL: Pick<TProjectDALFactory, "findProjectGhostUser" | "findProjectById">;
|
||||
projectDAL: Pick<TProjectDALFactory, "findProjectGhostUser" | "findProjectById" | "findById">;
|
||||
projectBotDAL: Pick<TProjectBotDALFactory, "findOne">;
|
||||
groupProjectDAL: Pick<TGroupProjectDALFactory, "find">;
|
||||
orgService: Pick<TOrgServiceFactory, "createOrganization" | "findOrganizationById">;
|
||||
@@ -147,17 +146,8 @@ export const authSignupServiceFactory = ({
|
||||
firstName,
|
||||
lastName,
|
||||
providerAuthToken,
|
||||
salt,
|
||||
verifier,
|
||||
publicKey,
|
||||
protectedKey,
|
||||
protectedKeyIV,
|
||||
protectedKeyTag,
|
||||
organizationName,
|
||||
// attributionSource,
|
||||
encryptedPrivateKey,
|
||||
encryptedPrivateKeyIV,
|
||||
encryptedPrivateKeyTag,
|
||||
ip,
|
||||
userAgent,
|
||||
authorization,
|
||||
@@ -191,98 +181,18 @@ export const authSignupServiceFactory = ({
|
||||
}
|
||||
|
||||
const hashedPassword = await crypto.hashing().createHash(password, appCfg.SALT_ROUNDS);
|
||||
const privateKey = await getUserPrivateKey(password, {
|
||||
salt,
|
||||
protectedKey,
|
||||
protectedKeyIV,
|
||||
protectedKeyTag,
|
||||
encryptedPrivateKey,
|
||||
iv: encryptedPrivateKeyIV,
|
||||
tag: encryptedPrivateKeyTag,
|
||||
encryptionVersion: UserEncryption.V2
|
||||
});
|
||||
const { tag, encoding, ciphertext, iv } = crypto.encryption().symmetric().encryptWithRootEncryptionKey(privateKey);
|
||||
const updateduser = await authDAL.transaction(async (tx) => {
|
||||
const us = await userDAL.updateById(user.id, { firstName, lastName, isAccepted: true }, tx);
|
||||
if (!us) throw new Error("User not found");
|
||||
const systemGeneratedUserEncryptionKey = await userDAL.findUserEncKeyByUserId(us.id, tx);
|
||||
let userEncKey;
|
||||
|
||||
// below condition is true means this is system generated credentials
|
||||
// the private key is actually system generated password
|
||||
// thus we will re-encrypt the system generated private key with the new password
|
||||
// akhilmhdh: you may find this like why? The reason is simple we are moving away from e2ee and these are pieces of it
|
||||
// without a dummy key in place some things will break and backward compatiability too. 2025 we will be removing all these things
|
||||
if (
|
||||
systemGeneratedUserEncryptionKey &&
|
||||
!systemGeneratedUserEncryptionKey.hashedPassword &&
|
||||
systemGeneratedUserEncryptionKey.serverEncryptedPrivateKey &&
|
||||
systemGeneratedUserEncryptionKey.serverEncryptedPrivateKeyTag &&
|
||||
systemGeneratedUserEncryptionKey.serverEncryptedPrivateKeyIV &&
|
||||
systemGeneratedUserEncryptionKey.serverEncryptedPrivateKeyEncoding
|
||||
) {
|
||||
// get server generated password
|
||||
const serverGeneratedPassword = crypto
|
||||
.encryption()
|
||||
.symmetric()
|
||||
.decryptWithRootEncryptionKey({
|
||||
iv: systemGeneratedUserEncryptionKey.serverEncryptedPrivateKeyIV,
|
||||
tag: systemGeneratedUserEncryptionKey.serverEncryptedPrivateKeyTag,
|
||||
ciphertext: systemGeneratedUserEncryptionKey.serverEncryptedPrivateKey,
|
||||
keyEncoding: systemGeneratedUserEncryptionKey.serverEncryptedPrivateKeyEncoding as SecretKeyEncoding
|
||||
});
|
||||
const serverGeneratedPrivateKey = await getUserPrivateKey(serverGeneratedPassword, {
|
||||
...systemGeneratedUserEncryptionKey
|
||||
});
|
||||
const encKeys = await generateUserSrpKeys(email, password, {
|
||||
publicKey: systemGeneratedUserEncryptionKey.publicKey,
|
||||
privateKey: serverGeneratedPrivateKey
|
||||
});
|
||||
// now reencrypt server generated key with user provided password
|
||||
userEncKey = await userDAL.upsertUserEncryptionKey(
|
||||
us.id,
|
||||
{
|
||||
encryptionVersion: UserEncryption.V2,
|
||||
protectedKey: encKeys.protectedKey,
|
||||
protectedKeyIV: encKeys.protectedKeyIV,
|
||||
protectedKeyTag: encKeys.protectedKeyTag,
|
||||
publicKey: encKeys.publicKey,
|
||||
encryptedPrivateKey: encKeys.encryptedPrivateKey,
|
||||
iv: encKeys.encryptedPrivateKeyIV,
|
||||
tag: encKeys.encryptedPrivateKeyTag,
|
||||
salt: encKeys.salt,
|
||||
verifier: encKeys.verifier,
|
||||
hashedPassword,
|
||||
serverEncryptedPrivateKeyEncoding: encoding,
|
||||
serverEncryptedPrivateKeyTag: tag,
|
||||
serverEncryptedPrivateKeyIV: iv,
|
||||
serverEncryptedPrivateKey: ciphertext
|
||||
},
|
||||
tx
|
||||
);
|
||||
} else {
|
||||
userEncKey = await userDAL.upsertUserEncryptionKey(
|
||||
us.id,
|
||||
{
|
||||
encryptionVersion: UserEncryption.V2,
|
||||
salt,
|
||||
verifier,
|
||||
publicKey,
|
||||
protectedKey,
|
||||
protectedKeyIV,
|
||||
protectedKeyTag,
|
||||
encryptedPrivateKey,
|
||||
iv: encryptedPrivateKeyIV,
|
||||
tag: encryptedPrivateKeyTag,
|
||||
hashedPassword,
|
||||
serverEncryptedPrivateKeyEncoding: encoding,
|
||||
serverEncryptedPrivateKeyTag: tag,
|
||||
serverEncryptedPrivateKeyIV: iv,
|
||||
serverEncryptedPrivateKey: ciphertext
|
||||
},
|
||||
tx
|
||||
);
|
||||
}
|
||||
const userEncKey = await userDAL.upsertUserEncryptionKey(
|
||||
us.id,
|
||||
{
|
||||
encryptionVersion: UserEncryption.V2,
|
||||
hashedPassword
|
||||
},
|
||||
tx
|
||||
);
|
||||
|
||||
// If it's SAML Auth and the organization ID is present, we should check if the user has a pending invite for this org, and accept it
|
||||
if (
|
||||
@@ -400,19 +310,10 @@ export const authSignupServiceFactory = ({
|
||||
const completeAccountInvite = async ({
|
||||
email,
|
||||
ip,
|
||||
salt,
|
||||
password,
|
||||
verifier,
|
||||
firstName,
|
||||
publicKey,
|
||||
userAgent,
|
||||
lastName,
|
||||
protectedKey,
|
||||
protectedKeyIV,
|
||||
protectedKeyTag,
|
||||
encryptedPrivateKey,
|
||||
encryptedPrivateKeyIV,
|
||||
encryptedPrivateKeyTag,
|
||||
authorization
|
||||
}: TCompleteAccountInviteDTO) => {
|
||||
const sanitizedEmail = email.trim().toLowerCase();
|
||||
@@ -437,94 +338,17 @@ export const authSignupServiceFactory = ({
|
||||
|
||||
const appCfg = getConfig();
|
||||
const hashedPassword = await crypto.hashing().createHash(password, appCfg.SALT_ROUNDS);
|
||||
const privateKey = await getUserPrivateKey(password, {
|
||||
salt,
|
||||
protectedKey,
|
||||
protectedKeyIV,
|
||||
protectedKeyTag,
|
||||
encryptedPrivateKey,
|
||||
iv: encryptedPrivateKeyIV,
|
||||
tag: encryptedPrivateKeyTag,
|
||||
encryptionVersion: 2
|
||||
});
|
||||
const { tag, encoding, ciphertext, iv } = crypto.encryption().symmetric().encryptWithRootEncryptionKey(privateKey);
|
||||
const updateduser = await authDAL.transaction(async (tx) => {
|
||||
const us = await userDAL.updateById(user.id, { firstName, lastName, isAccepted: true }, tx);
|
||||
if (!us) throw new Error("User not found");
|
||||
const systemGeneratedUserEncryptionKey = await userDAL.findUserEncKeyByUserId(us.id, tx);
|
||||
let userEncKey;
|
||||
// this means this is system generated credentials
|
||||
// now replace the private key
|
||||
if (
|
||||
systemGeneratedUserEncryptionKey &&
|
||||
!systemGeneratedUserEncryptionKey.hashedPassword &&
|
||||
systemGeneratedUserEncryptionKey.serverEncryptedPrivateKey &&
|
||||
systemGeneratedUserEncryptionKey.serverEncryptedPrivateKeyTag &&
|
||||
systemGeneratedUserEncryptionKey.serverEncryptedPrivateKeyIV &&
|
||||
systemGeneratedUserEncryptionKey.serverEncryptedPrivateKeyEncoding
|
||||
) {
|
||||
// get server generated password
|
||||
const serverGeneratedPassword = crypto
|
||||
.encryption()
|
||||
.symmetric()
|
||||
.decryptWithRootEncryptionKey({
|
||||
iv: systemGeneratedUserEncryptionKey.serverEncryptedPrivateKeyIV,
|
||||
tag: systemGeneratedUserEncryptionKey.serverEncryptedPrivateKeyTag,
|
||||
ciphertext: systemGeneratedUserEncryptionKey.serverEncryptedPrivateKey,
|
||||
keyEncoding: systemGeneratedUserEncryptionKey.serverEncryptedPrivateKeyEncoding as SecretKeyEncoding
|
||||
});
|
||||
const serverGeneratedPrivateKey = await getUserPrivateKey(serverGeneratedPassword, {
|
||||
...systemGeneratedUserEncryptionKey
|
||||
});
|
||||
const encKeys = await generateUserSrpKeys(sanitizedEmail, password, {
|
||||
publicKey: systemGeneratedUserEncryptionKey.publicKey,
|
||||
privateKey: serverGeneratedPrivateKey
|
||||
});
|
||||
// now reencrypt server generated key with user provided password
|
||||
userEncKey = await userDAL.upsertUserEncryptionKey(
|
||||
us.id,
|
||||
{
|
||||
encryptionVersion: 2,
|
||||
protectedKey: encKeys.protectedKey,
|
||||
protectedKeyIV: encKeys.protectedKeyIV,
|
||||
protectedKeyTag: encKeys.protectedKeyTag,
|
||||
publicKey: encKeys.publicKey,
|
||||
encryptedPrivateKey: encKeys.encryptedPrivateKey,
|
||||
iv: encKeys.encryptedPrivateKeyIV,
|
||||
tag: encKeys.encryptedPrivateKeyTag,
|
||||
salt: encKeys.salt,
|
||||
verifier: encKeys.verifier,
|
||||
hashedPassword,
|
||||
serverEncryptedPrivateKeyEncoding: encoding,
|
||||
serverEncryptedPrivateKeyTag: tag,
|
||||
serverEncryptedPrivateKeyIV: iv,
|
||||
serverEncryptedPrivateKey: ciphertext
|
||||
},
|
||||
tx
|
||||
);
|
||||
} else {
|
||||
userEncKey = await userDAL.upsertUserEncryptionKey(
|
||||
us.id,
|
||||
{
|
||||
encryptionVersion: UserEncryption.V2,
|
||||
salt,
|
||||
verifier,
|
||||
publicKey,
|
||||
protectedKey,
|
||||
protectedKeyIV,
|
||||
protectedKeyTag,
|
||||
encryptedPrivateKey,
|
||||
iv: encryptedPrivateKeyIV,
|
||||
tag: encryptedPrivateKeyTag,
|
||||
hashedPassword,
|
||||
serverEncryptedPrivateKeyEncoding: encoding,
|
||||
serverEncryptedPrivateKeyTag: tag,
|
||||
serverEncryptedPrivateKeyIV: iv,
|
||||
serverEncryptedPrivateKey: ciphertext
|
||||
},
|
||||
tx
|
||||
);
|
||||
}
|
||||
const userEncKey = await userDAL.upsertUserEncryptionKey(
|
||||
us.id,
|
||||
{
|
||||
encryptionVersion: 2,
|
||||
hashedPassword
|
||||
},
|
||||
tx
|
||||
);
|
||||
|
||||
const updatedMembersips = await orgDAL.updateMembership(
|
||||
{ inviteEmail: sanitizedEmail, status: OrgMembershipStatus.Invited },
|
||||
|
||||
@@ -3,15 +3,6 @@ export type TCompleteAccountSignupDTO = {
|
||||
password: string;
|
||||
firstName: string;
|
||||
lastName?: string;
|
||||
protectedKey: string;
|
||||
protectedKeyIV: string;
|
||||
protectedKeyTag: string;
|
||||
publicKey: string;
|
||||
encryptedPrivateKey: string;
|
||||
encryptedPrivateKeyIV: string;
|
||||
encryptedPrivateKeyTag: string;
|
||||
salt: string;
|
||||
verifier: string;
|
||||
organizationName?: string;
|
||||
providerAuthToken?: string | null;
|
||||
attributionSource?: string | undefined;
|
||||
@@ -26,15 +17,6 @@ export type TCompleteAccountInviteDTO = {
|
||||
password: string;
|
||||
firstName: string;
|
||||
lastName?: string;
|
||||
protectedKey: string;
|
||||
protectedKeyIV: string;
|
||||
protectedKeyTag: string;
|
||||
publicKey: string;
|
||||
encryptedPrivateKey: string;
|
||||
encryptedPrivateKeyIV: string;
|
||||
encryptedPrivateKeyTag: string;
|
||||
salt: string;
|
||||
verifier: string;
|
||||
ip: string;
|
||||
userAgent: string;
|
||||
authorization: string;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
|
||||
import { ActionProjectType, ProjectMembershipRole, SecretKeyEncoding, TGroups } from "@app/db/schemas";
|
||||
import { ActionProjectType, ProjectMembershipRole, ProjectVersion, SecretKeyEncoding, TGroups } from "@app/db/schemas";
|
||||
import { TListProjectGroupUsersDTO } from "@app/ee/services/group/group-types";
|
||||
import {
|
||||
constructPermissionErrorMessage,
|
||||
@@ -188,7 +188,7 @@ export const groupProjectServiceFactory = ({
|
||||
// other groups that are in the project
|
||||
const groupMembers = await userGroupMembershipDAL.findGroupMembersNotInProject(group!.id, project.id, tx);
|
||||
|
||||
if (groupMembers.length) {
|
||||
if (groupMembers.length && (project.version === ProjectVersion.V1 || project.version === ProjectVersion.V2)) {
|
||||
const ghostUser = await projectDAL.findProjectGhostUser(project.id, tx);
|
||||
|
||||
if (!ghostUser) {
|
||||
@@ -205,6 +205,12 @@ export const groupProjectServiceFactory = ({
|
||||
});
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -231,6 +237,12 @@ export const groupProjectServiceFactory = ({
|
||||
});
|
||||
|
||||
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()
|
||||
|
||||
@@ -1,19 +1,16 @@
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
|
||||
import { ProjectMembershipRole, ProjectVersion, SecretKeyEncoding } from "@app/db/schemas";
|
||||
import { ProjectMembershipRole, ProjectVersion } from "@app/db/schemas";
|
||||
import { OrgPermissionAdminConsoleAction, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
||||
import { crypto } from "@app/lib/crypto/cryptography";
|
||||
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
||||
|
||||
import { TProjectDALFactory } from "../project/project-dal";
|
||||
import { assignWorkspaceKeysToMembers } from "../project/project-fns";
|
||||
import { TProjectBotDALFactory } from "../project-bot/project-bot-dal";
|
||||
import { TProjectKeyDALFactory } from "../project-key/project-key-dal";
|
||||
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 { TUserDALFactory } from "../user/user-dal";
|
||||
import { TAccessProjectDTO, TListOrgProjectsDTO } from "./org-admin-types";
|
||||
|
||||
type TOrgAdminServiceFactoryDep = {
|
||||
@@ -25,7 +22,6 @@ type TOrgAdminServiceFactoryDep = {
|
||||
>;
|
||||
projectKeyDAL: Pick<TProjectKeyDALFactory, "findLatestProjectKey" | "create">;
|
||||
projectBotDAL: Pick<TProjectBotDALFactory, "findOne">;
|
||||
userDAL: Pick<TUserDALFactory, "findUserEncKeyByUserId">;
|
||||
projectUserMembershipRoleDAL: Pick<TProjectUserMembershipRoleDALFactory, "create" | "delete">;
|
||||
smtpService: Pick<TSmtpService, "sendMail">;
|
||||
};
|
||||
@@ -38,7 +34,6 @@ export const orgAdminServiceFactory = ({
|
||||
projectMembershipDAL,
|
||||
projectKeyDAL,
|
||||
projectBotDAL,
|
||||
userDAL,
|
||||
projectUserMembershipRoleDAL,
|
||||
smtpService
|
||||
}: TOrgAdminServiceFactoryDep) => {
|
||||
@@ -83,7 +78,7 @@ export const orgAdminServiceFactory = ({
|
||||
actorAuthMethod,
|
||||
projectId
|
||||
}: TAccessProjectDTO) => {
|
||||
const { permission, membership } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
actor,
|
||||
actorId,
|
||||
actorOrgId,
|
||||
@@ -98,8 +93,10 @@ export const orgAdminServiceFactory = ({
|
||||
const project = await projectDAL.findOne({ id: projectId, orgId: actorOrgId });
|
||||
if (!project) throw new NotFoundError({ message: `Project with ID '${projectId}' not found` });
|
||||
|
||||
if (project.version === ProjectVersion.V1) {
|
||||
throw new BadRequestError({ message: "Please upgrade your project on your dashboard" });
|
||||
if (project.version === ProjectVersion.V1 || project.version === ProjectVersion.V2) {
|
||||
throw new BadRequestError({
|
||||
message: `Project '${project.name}' is a legacy project and must be upgraded before accessing it through the admin console.`
|
||||
});
|
||||
}
|
||||
|
||||
// check already there exist a membership if there return it
|
||||
@@ -144,30 +141,6 @@ export const orgAdminServiceFactory = ({
|
||||
});
|
||||
}
|
||||
|
||||
const botPrivateKey = crypto
|
||||
.encryption()
|
||||
.symmetric()
|
||||
.decryptWithRootEncryptionKey({
|
||||
keyEncoding: bot.keyEncoding as SecretKeyEncoding,
|
||||
iv: bot.iv,
|
||||
tag: bot.tag,
|
||||
ciphertext: bot.encryptedPrivateKey
|
||||
});
|
||||
|
||||
const userEncryptionKey = await userDAL.findUserEncKeyByUserId(actorId);
|
||||
if (!userEncryptionKey)
|
||||
throw new NotFoundError({ message: `User encryption key for user with ID '${actorId}' not found` });
|
||||
const [newWsMember] = assignWorkspaceKeysToMembers({
|
||||
decryptKey: ghostUserLatestKey,
|
||||
userPrivateKey: botPrivateKey,
|
||||
members: [
|
||||
{
|
||||
orgMembershipId: membership.id,
|
||||
userPublicKey: userEncryptionKey.publicKey
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
const updatedMembership = await projectMembershipDAL.transaction(async (tx) => {
|
||||
const newProjectMembership = await projectMembershipDAL.create(
|
||||
{
|
||||
@@ -181,16 +154,6 @@ export const orgAdminServiceFactory = ({
|
||||
tx
|
||||
);
|
||||
|
||||
await projectKeyDAL.create(
|
||||
{
|
||||
encryptedKey: newWsMember.workspaceEncryptedKey,
|
||||
nonce: newWsMember.workspaceEncryptedNonce,
|
||||
senderId: ghostUser.id,
|
||||
receiverId: actorId,
|
||||
projectId
|
||||
},
|
||||
tx
|
||||
);
|
||||
return newProjectMembership;
|
||||
});
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
OrgMembershipStatus,
|
||||
ProjectMembershipRole,
|
||||
ProjectVersion,
|
||||
SecretKeyEncoding,
|
||||
TableName,
|
||||
TProjectMemberships,
|
||||
TProjectUserMembershipRolesInsert,
|
||||
@@ -58,8 +57,6 @@ import { TAuthTokenServiceFactory } from "../auth-token/auth-token-service";
|
||||
import { TokenType } from "../auth-token/auth-token-types";
|
||||
import { TIdentityMetadataDALFactory } from "../identity/identity-metadata-dal";
|
||||
import { TProjectDALFactory } from "../project/project-dal";
|
||||
import { assignWorkspaceKeysToMembers, createProjectKey } from "../project/project-fns";
|
||||
import { TProjectBotDALFactory } from "../project-bot/project-bot-dal";
|
||||
import { TProjectBotServiceFactory } from "../project-bot/project-bot-service";
|
||||
import { TProjectKeyDALFactory } from "../project-key/project-key-dal";
|
||||
import { TProjectMembershipDALFactory } from "../project-membership/project-membership-dal";
|
||||
@@ -137,7 +134,6 @@ type TOrgServiceFactoryDep = {
|
||||
>;
|
||||
projectUserAdditionalPrivilegeDAL: Pick<TProjectUserAdditionalPrivilegeDALFactory, "delete">;
|
||||
projectRoleDAL: Pick<TProjectRoleDALFactory, "find">;
|
||||
projectBotDAL: Pick<TProjectBotDALFactory, "findOne" | "updateById">;
|
||||
projectUserMembershipRoleDAL: Pick<TProjectUserMembershipRoleDALFactory, "insertMany" | "create">;
|
||||
projectBotService: Pick<TProjectBotServiceFactory, "getBotKey">;
|
||||
loginService: Pick<TAuthLoginFactory, "generateUserTokens">;
|
||||
@@ -169,7 +165,6 @@ export const orgServiceFactory = ({
|
||||
projectRoleDAL,
|
||||
samlConfigDAL,
|
||||
oidcConfigDAL,
|
||||
projectBotDAL,
|
||||
projectUserMembershipRoleDAL,
|
||||
identityMetadataDAL,
|
||||
projectBotService,
|
||||
@@ -287,15 +282,7 @@ export const orgServiceFactory = ({
|
||||
user.id,
|
||||
{
|
||||
encryptionVersion: 2,
|
||||
protectedKey: encKeys.protectedKey,
|
||||
protectedKeyIV: encKeys.protectedKeyIV,
|
||||
protectedKeyTag: encKeys.protectedKeyTag,
|
||||
publicKey: encKeys.publicKey,
|
||||
encryptedPrivateKey: encKeys.encryptedPrivateKey,
|
||||
iv: encKeys.encryptedPrivateKeyIV,
|
||||
tag: encKeys.encryptedPrivateKeyTag,
|
||||
salt: encKeys.salt,
|
||||
verifier: encKeys.verifier
|
||||
publicKey: encKeys.publicKey
|
||||
},
|
||||
tx
|
||||
);
|
||||
@@ -885,29 +872,10 @@ export const orgServiceFactory = ({
|
||||
// So what we do is we generate a random secure password and then encrypt it with a random pub-private key
|
||||
// Then when user sign in (as login is not possible as isAccepted is false) we rencrypt the private key with the user password
|
||||
if (!inviteeUser || (inviteeUser && !inviteeUser?.isAccepted && !existingEncrytionKey)) {
|
||||
const serverGeneratedPassword = crypto.randomBytes(32).toString("hex");
|
||||
const { tag, encoding, ciphertext, iv } = crypto
|
||||
.encryption()
|
||||
.symmetric()
|
||||
.encryptWithRootEncryptionKey(serverGeneratedPassword);
|
||||
const encKeys = await generateUserSrpKeys(inviteeEmail, serverGeneratedPassword);
|
||||
await userDAL.createUserEncryption(
|
||||
{
|
||||
userId: inviteeUserId,
|
||||
encryptionVersion: 2,
|
||||
protectedKey: encKeys.protectedKey,
|
||||
protectedKeyIV: encKeys.protectedKeyIV,
|
||||
protectedKeyTag: encKeys.protectedKeyTag,
|
||||
publicKey: encKeys.publicKey,
|
||||
encryptedPrivateKey: encKeys.encryptedPrivateKey,
|
||||
iv: encKeys.encryptedPrivateKeyIV,
|
||||
tag: encKeys.encryptedPrivateKeyTag,
|
||||
salt: encKeys.salt,
|
||||
verifier: encKeys.verifier,
|
||||
serverEncryptedPrivateKeyEncoding: encoding,
|
||||
serverEncryptedPrivateKeyTag: tag,
|
||||
serverEncryptedPrivateKeyIV: iv,
|
||||
serverEncryptedPrivateKey: ciphertext
|
||||
encryptionVersion: 2
|
||||
},
|
||||
tx
|
||||
);
|
||||
@@ -1069,106 +1037,6 @@ export const orgServiceFactory = ({
|
||||
|
||||
const customRolesGroupBySlug = groupBy(customRoles, ({ slug }) => slug);
|
||||
|
||||
// this will auto generate bot
|
||||
const { botKey, bot: autoGeneratedBot } = await projectBotService.getBotKey(projectId, true);
|
||||
|
||||
const ghostUser = await projectDAL.findProjectGhostUser(projectId, tx);
|
||||
let ghostUserId = ghostUser?.id;
|
||||
|
||||
// backfill missing ghost user
|
||||
if (!ghostUserId) {
|
||||
const newGhostUser = await addGhostUser(project.orgId, tx);
|
||||
const projectMembership = await projectMembershipDAL.create(
|
||||
{
|
||||
userId: newGhostUser.user.id,
|
||||
projectId: project.id
|
||||
},
|
||||
tx
|
||||
);
|
||||
await projectUserMembershipRoleDAL.create(
|
||||
{ projectMembershipId: projectMembership.id, role: ProjectMembershipRole.Admin },
|
||||
tx
|
||||
);
|
||||
|
||||
const { key: encryptedProjectKey, iv: encryptedProjectKeyIv } = createProjectKey({
|
||||
publicKey: newGhostUser.keys.publicKey,
|
||||
privateKey: newGhostUser.keys.plainPrivateKey,
|
||||
plainProjectKey: botKey
|
||||
});
|
||||
|
||||
// 4. Save the project key for the ghost user.
|
||||
await projectKeyDAL.create(
|
||||
{
|
||||
projectId: project.id,
|
||||
receiverId: newGhostUser.user.id,
|
||||
encryptedKey: encryptedProjectKey,
|
||||
nonce: encryptedProjectKeyIv,
|
||||
senderId: newGhostUser.user.id
|
||||
},
|
||||
tx
|
||||
);
|
||||
|
||||
const { iv, tag, ciphertext, encoding, algorithm } = crypto
|
||||
.encryption()
|
||||
.symmetric()
|
||||
.encryptWithRootEncryptionKey(newGhostUser.keys.plainPrivateKey);
|
||||
if (autoGeneratedBot) {
|
||||
await projectBotDAL.updateById(
|
||||
autoGeneratedBot.id,
|
||||
{
|
||||
tag,
|
||||
iv,
|
||||
encryptedProjectKey,
|
||||
encryptedProjectKeyNonce: encryptedProjectKeyIv,
|
||||
encryptedPrivateKey: ciphertext,
|
||||
isActive: true,
|
||||
publicKey: newGhostUser.keys.publicKey,
|
||||
senderId: newGhostUser.user.id,
|
||||
algorithm,
|
||||
keyEncoding: encoding
|
||||
},
|
||||
tx
|
||||
);
|
||||
}
|
||||
ghostUserId = newGhostUser.user.id;
|
||||
}
|
||||
|
||||
const bot = await projectBotDAL.findOne({ projectId }, tx);
|
||||
if (!bot) {
|
||||
throw new NotFoundError({
|
||||
name: "InviteUser",
|
||||
message: `Failed to find project bot for project with ID '${projectId}'`
|
||||
});
|
||||
}
|
||||
|
||||
const ghostUserLatestKey = await projectKeyDAL.findLatestProjectKey(ghostUserId, projectId, tx);
|
||||
if (!ghostUserLatestKey) {
|
||||
throw new NotFoundError({
|
||||
name: "InviteUser",
|
||||
message: `Failed to find project owner's latest key for project with ID '${projectId}'`
|
||||
});
|
||||
}
|
||||
|
||||
const botPrivateKey = crypto
|
||||
.encryption()
|
||||
.symmetric()
|
||||
.decryptWithRootEncryptionKey({
|
||||
keyEncoding: bot.keyEncoding as SecretKeyEncoding,
|
||||
iv: bot.iv,
|
||||
tag: bot.tag,
|
||||
ciphertext: bot.encryptedPrivateKey
|
||||
});
|
||||
|
||||
const newWsMembers = assignWorkspaceKeysToMembers({
|
||||
decryptKey: ghostUserLatestKey,
|
||||
userPrivateKey: botPrivateKey,
|
||||
members: userWithEncryptionKeyInvitedToProject.map((userEnc) => ({
|
||||
orgMembershipId: userEnc.userId,
|
||||
projectMembershipRole: ProjectMembershipRole.Admin,
|
||||
userPublicKey: userEnc.publicKey
|
||||
}))
|
||||
});
|
||||
|
||||
const projectMemberships = await projectMembershipDAL.insertMany(
|
||||
userWithEncryptionKeyInvitedToProject.map((userEnc) => ({
|
||||
projectId,
|
||||
@@ -1191,16 +1059,6 @@ export const orgServiceFactory = ({
|
||||
});
|
||||
await projectUserMembershipRoleDAL.insertMany(sanitizedProjectMembershipRoles, tx);
|
||||
|
||||
await projectKeyDAL.insertMany(
|
||||
newWsMembers.map((el) => ({
|
||||
encryptedKey: el.workspaceEncryptedKey,
|
||||
nonce: el.workspaceEncryptedNonce,
|
||||
senderId: ghostUserId,
|
||||
receiverId: el.orgMembershipId,
|
||||
projectId
|
||||
})),
|
||||
tx
|
||||
);
|
||||
mailsForProjectInvitation.push({
|
||||
email: userWithEncryptionKeyInvitedToProject
|
||||
.filter((el) => !userIdsWithOrgInvitation.has(el.userId))
|
||||
|
||||
@@ -42,6 +42,13 @@ export const getBotKeyFnFactory = (
|
||||
message: `Project bot not found for project with ID '${projectId}'. Please ask an administrator to log-in to the Infisical Console.`
|
||||
});
|
||||
}
|
||||
|
||||
if (!projectV1Keys.senderPublicKey) {
|
||||
throw new NotFoundError({
|
||||
message: `Project bot not found for project with ID '${projectId}'. Please ask an administrator to log-in to the Infisical Console and upgrade the project.`
|
||||
});
|
||||
}
|
||||
|
||||
let userPrivateKey = "";
|
||||
if (
|
||||
projectV1Keys?.serverEncryptedPrivateKey &&
|
||||
|
||||
@@ -14,7 +14,7 @@ export const projectKeyDALFactory = (db: TDbClient) => {
|
||||
userId: string,
|
||||
projectId: string,
|
||||
tx?: Knex
|
||||
): Promise<(TProjectKeys & { sender: { publicKey: string } }) | undefined> => {
|
||||
): Promise<(TProjectKeys & { sender: { publicKey?: string } }) | undefined> => {
|
||||
try {
|
||||
const projectKey = await (tx || db.replicaNode())(TableName.ProjectKeys)
|
||||
.join(TableName.Users, `${TableName.ProjectKeys}.senderId`, `${TableName.Users}.id`)
|
||||
@@ -25,7 +25,7 @@ export const projectKeyDALFactory = (db: TDbClient) => {
|
||||
.select(db.ref("publicKey").withSchema(TableName.UserEncryptionKey))
|
||||
.first();
|
||||
if (projectKey) {
|
||||
return { ...projectKey, sender: { publicKey: projectKey.publicKey } };
|
||||
return { ...projectKey, sender: { publicKey: projectKey.publicKey || undefined } };
|
||||
}
|
||||
} catch (error) {
|
||||
throw new DatabaseError({ error, name: "Find latest project key" });
|
||||
|
||||
@@ -10,6 +10,10 @@ import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||
import { AddUserToWsDTO, TBootstrapSshProjectDTO } from "./project-types";
|
||||
|
||||
export const assignWorkspaceKeysToMembers = ({ members, decryptKey, userPrivateKey }: AddUserToWsDTO) => {
|
||||
if (!decryptKey.sender.publicKey) {
|
||||
throw new Error("Decrypt key sender public key not found");
|
||||
}
|
||||
|
||||
const plaintextProjectKey = crypto.encryption().asymmetric().decrypt({
|
||||
ciphertext: decryptKey.encryptedKey,
|
||||
nonce: decryptKey.nonce,
|
||||
|
||||
@@ -121,6 +121,10 @@ export const projectQueueFactory = ({
|
||||
tag: data.encryptedPrivateKey.encryptedKeyTag
|
||||
});
|
||||
|
||||
if (!oldProjectKey.sender.publicKey) {
|
||||
throw new Error("Old project key sender public key not found");
|
||||
}
|
||||
|
||||
const decryptedPlainProjectKey = crypto.encryption().asymmetric().decrypt({
|
||||
ciphertext: oldProjectKey.encryptedKey,
|
||||
nonce: oldProjectKey.nonce,
|
||||
@@ -290,6 +294,10 @@ export const projectQueueFactory = ({
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!user.publicKey) {
|
||||
throw new Error(`User with ID ${key.receiverId} has no public key during upgrade.`);
|
||||
}
|
||||
|
||||
const [newMember] = assignWorkspaceKeysToMembers({
|
||||
decryptKey: ghostUserLatestKey,
|
||||
userPrivateKey: ghostUser.keys.plainPrivateKey,
|
||||
|
||||
@@ -55,13 +55,10 @@ import { validateMicrosoftTeamsChannelsSchema } from "../microsoft-teams/microso
|
||||
import { TMicrosoftTeamsIntegrationDALFactory } from "../microsoft-teams/microsoft-teams-integration-dal";
|
||||
import { TProjectMicrosoftTeamsConfigDALFactory } from "../microsoft-teams/project-microsoft-teams-config-dal";
|
||||
import { TOrgDALFactory } from "../org/org-dal";
|
||||
import { TOrgServiceFactory } from "../org/org-service";
|
||||
import { TPkiAlertDALFactory } from "../pki-alert/pki-alert-dal";
|
||||
import { TPkiCollectionDALFactory } from "../pki-collection/pki-collection-dal";
|
||||
import { TProjectBotDALFactory } from "../project-bot/project-bot-dal";
|
||||
import { TProjectBotServiceFactory } from "../project-bot/project-bot-service";
|
||||
import { TProjectEnvDALFactory } from "../project-env/project-env-dal";
|
||||
import { TProjectKeyDALFactory } from "../project-key/project-key-dal";
|
||||
import { TProjectMembershipDALFactory } from "../project-membership/project-membership-dal";
|
||||
import { TProjectUserMembershipRoleDALFactory } from "../project-membership/project-user-membership-role-dal";
|
||||
import { TProjectRoleDALFactory } from "../project-role/project-role-dal";
|
||||
@@ -78,7 +75,7 @@ import { SmtpTemplates, TSmtpService } from "../smtp/smtp-service";
|
||||
import { TUserDALFactory } from "../user/user-dal";
|
||||
import { WorkflowIntegration, WorkflowIntegrationStatus } from "../workflow-integration/workflow-integration-types";
|
||||
import { TProjectDALFactory } from "./project-dal";
|
||||
import { assignWorkspaceKeysToMembers, bootstrapSshProject, createProjectKey } from "./project-fns";
|
||||
import { bootstrapSshProject } from "./project-fns";
|
||||
import { TProjectQueueFactory } from "./project-queue";
|
||||
import { TProjectSshConfigDALFactory } from "./project-ssh-config-dal";
|
||||
import {
|
||||
@@ -123,6 +120,7 @@ export const DEFAULT_PROJECT_ENVS = [
|
||||
|
||||
type TProjectServiceFactoryDep = {
|
||||
projectDAL: TProjectDALFactory;
|
||||
identityProjectDAL: Pick<TIdentityProjectDALFactory, "create">;
|
||||
projectSshConfigDAL: Pick<TProjectSshConfigDALFactory, "transaction" | "create" | "findOne" | "updateById">;
|
||||
projectQueue: TProjectQueueFactory;
|
||||
userDAL: TUserDALFactory;
|
||||
@@ -132,9 +130,7 @@ type TProjectServiceFactoryDep = {
|
||||
secretV2BridgeDAL: Pick<TSecretV2BridgeDALFactory, "find">;
|
||||
projectEnvDAL: Pick<TProjectEnvDALFactory, "insertMany" | "find">;
|
||||
identityOrgMembershipDAL: TIdentityOrgDALFactory;
|
||||
identityProjectDAL: TIdentityProjectDALFactory;
|
||||
identityProjectMembershipRoleDAL: Pick<TIdentityProjectMembershipRoleDALFactory, "create">;
|
||||
projectKeyDAL: Pick<TProjectKeyDALFactory, "create" | "findLatestProjectKey" | "delete" | "find" | "insertMany">;
|
||||
projectMembershipDAL: Pick<
|
||||
TProjectMembershipDALFactory,
|
||||
"create" | "findProjectGhostUser" | "findOne" | "delete" | "findAllProjectMembers"
|
||||
@@ -167,12 +163,10 @@ type TProjectServiceFactoryDep = {
|
||||
sshHostDAL: Pick<TSshHostDALFactory, "find" | "findSshHostsWithLoginMappings">;
|
||||
sshHostGroupDAL: Pick<TSshHostGroupDALFactory, "find" | "findSshHostGroupsWithLoginMappings">;
|
||||
permissionService: TPermissionServiceFactory;
|
||||
orgService: Pick<TOrgServiceFactory, "addGhostUser">;
|
||||
licenseService: Pick<TLicenseServiceFactory, "getPlan" | "invalidateGetPlan">;
|
||||
smtpService: Pick<TSmtpService, "sendMail">;
|
||||
orgDAL: Pick<TOrgDALFactory, "findOne">;
|
||||
keyStore: Pick<TKeyStoreFactory, "deleteItem">;
|
||||
projectBotDAL: Pick<TProjectBotDALFactory, "create">;
|
||||
projectRoleDAL: Pick<TProjectRoleDALFactory, "find" | "insertMany" | "delete">;
|
||||
kmsService: Pick<
|
||||
TKmsServiceFactory,
|
||||
@@ -196,27 +190,25 @@ export const projectServiceFactory = ({
|
||||
secretDAL,
|
||||
secretV2BridgeDAL,
|
||||
projectQueue,
|
||||
projectKeyDAL,
|
||||
permissionService,
|
||||
projectBotService,
|
||||
orgDAL,
|
||||
userDAL,
|
||||
folderDAL,
|
||||
orgService,
|
||||
identityProjectDAL,
|
||||
identityOrgMembershipDAL,
|
||||
projectMembershipDAL,
|
||||
projectEnvDAL,
|
||||
licenseService,
|
||||
projectUserMembershipRoleDAL,
|
||||
projectRoleDAL,
|
||||
identityProjectMembershipRoleDAL,
|
||||
certificateAuthorityDAL,
|
||||
certificateDAL,
|
||||
certificateTemplateDAL,
|
||||
pkiCollectionDAL,
|
||||
pkiAlertDAL,
|
||||
pkiSubscriberDAL,
|
||||
identityProjectDAL,
|
||||
identityProjectMembershipRoleDAL,
|
||||
sshCertificateAuthorityDAL,
|
||||
sshCertificateAuthoritySecretDAL,
|
||||
sshCertificateDAL,
|
||||
@@ -225,7 +217,6 @@ export const projectServiceFactory = ({
|
||||
sshHostGroupDAL,
|
||||
keyStore,
|
||||
kmsService,
|
||||
projectBotDAL,
|
||||
projectSlackConfigDAL,
|
||||
projectMicrosoftTeamsConfigDAL,
|
||||
slackIntegrationDAL,
|
||||
@@ -253,7 +244,7 @@ export const projectServiceFactory = ({
|
||||
type = ProjectType.SecretManager
|
||||
}: TCreateProjectDTO) => {
|
||||
const organization = await orgDAL.findOne({ id: actorOrgId });
|
||||
const { permission, membership: orgMembership } = await permissionService.getOrgPermission(
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
actor,
|
||||
actorId,
|
||||
organization.id,
|
||||
@@ -277,7 +268,6 @@ export const projectServiceFactory = ({
|
||||
message: "Failed to create workspace due to plan limit reached. Upgrade plan to add more workspaces."
|
||||
});
|
||||
}
|
||||
const ghostUser = await orgService.addGhostUser(organization.id, tx);
|
||||
|
||||
if (kmsKeyId) {
|
||||
const kms = await kmsService.getKmsById(kmsKeyId, tx);
|
||||
@@ -329,19 +319,6 @@ export const projectServiceFactory = ({
|
||||
});
|
||||
}
|
||||
|
||||
// set ghost user as admin of project
|
||||
const projectMembership = await projectMembershipDAL.create(
|
||||
{
|
||||
userId: ghostUser.user.id,
|
||||
projectId: project.id
|
||||
},
|
||||
tx
|
||||
);
|
||||
await projectUserMembershipRoleDAL.create(
|
||||
{ projectMembershipId: projectMembership.id, role: ProjectMembershipRole.Admin },
|
||||
tx
|
||||
);
|
||||
|
||||
// set default environments and root folder for provided environments
|
||||
let envs: TProjectEnvironments[] = [];
|
||||
if (projectTemplate) {
|
||||
@@ -374,55 +351,6 @@ export const projectServiceFactory = ({
|
||||
);
|
||||
}
|
||||
|
||||
// 3. Create a random key that we'll use as the project key.
|
||||
const { key: encryptedProjectKey, iv: encryptedProjectKeyIv } = createProjectKey({
|
||||
publicKey: ghostUser.keys.publicKey,
|
||||
privateKey: ghostUser.keys.plainPrivateKey
|
||||
});
|
||||
|
||||
// 4. Save the project key for the ghost user.
|
||||
await projectKeyDAL.create(
|
||||
{
|
||||
projectId: project.id,
|
||||
receiverId: ghostUser.user.id,
|
||||
encryptedKey: encryptedProjectKey,
|
||||
nonce: encryptedProjectKeyIv,
|
||||
senderId: ghostUser.user.id
|
||||
},
|
||||
tx
|
||||
);
|
||||
|
||||
const { iv, tag, ciphertext, encoding, algorithm } = crypto
|
||||
.encryption()
|
||||
.symmetric()
|
||||
.encryptWithRootEncryptionKey(ghostUser.keys.plainPrivateKey);
|
||||
|
||||
// 5. Create & a bot for the project
|
||||
await projectBotDAL.create(
|
||||
{
|
||||
name: "Infisical Bot (Ghost)",
|
||||
projectId: project.id,
|
||||
tag,
|
||||
iv,
|
||||
encryptedProjectKey,
|
||||
encryptedProjectKeyNonce: encryptedProjectKeyIv,
|
||||
encryptedPrivateKey: ciphertext,
|
||||
isActive: true,
|
||||
publicKey: ghostUser.keys.publicKey,
|
||||
senderId: ghostUser.user.id,
|
||||
algorithm,
|
||||
keyEncoding: encoding
|
||||
},
|
||||
tx
|
||||
);
|
||||
|
||||
// Find the ghost users latest key
|
||||
const latestKey = await projectKeyDAL.findLatestProjectKey(ghostUser.user.id, project.id, tx);
|
||||
|
||||
if (!latestKey) {
|
||||
throw new Error("Latest key not found for user");
|
||||
}
|
||||
|
||||
// If the project is being created by a user, add the user to the project as an admin
|
||||
if (actor === ActorType.USER) {
|
||||
// Find public key of user
|
||||
@@ -432,17 +360,6 @@ export const projectServiceFactory = ({
|
||||
throw new Error("User not found");
|
||||
}
|
||||
|
||||
const [projectAdmin] = assignWorkspaceKeysToMembers({
|
||||
decryptKey: latestKey,
|
||||
userPrivateKey: ghostUser.keys.plainPrivateKey,
|
||||
members: [
|
||||
{
|
||||
userPublicKey: user.publicKey,
|
||||
orgMembershipId: orgMembership.id
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
// Create a membership for the user
|
||||
const userProjectMembership = await projectMembershipDAL.create(
|
||||
{
|
||||
@@ -455,18 +372,6 @@ export const projectServiceFactory = ({
|
||||
{ projectMembershipId: userProjectMembership.id, role: ProjectMembershipRole.Admin },
|
||||
tx
|
||||
);
|
||||
|
||||
// Create a project key for the user
|
||||
await projectKeyDAL.create(
|
||||
{
|
||||
encryptedKey: projectAdmin.workspaceEncryptedKey,
|
||||
nonce: projectAdmin.workspaceEncryptedNonce,
|
||||
senderId: ghostUser.user.id,
|
||||
receiverId: user.id,
|
||||
projectId: project.id
|
||||
},
|
||||
tx
|
||||
);
|
||||
}
|
||||
|
||||
// If the project is being created by an identity, add the identity to the project as an admin
|
||||
|
||||
@@ -117,7 +117,7 @@ export type TUpgradeProjectDTO = {
|
||||
} & TProjectPermission;
|
||||
|
||||
export type AddUserToWsDTO = {
|
||||
decryptKey: TProjectKeys & { sender: { publicKey: string } };
|
||||
decryptKey: TProjectKeys & { sender: { publicKey?: string } };
|
||||
userPrivateKey: string;
|
||||
members: {
|
||||
orgMembershipId: string;
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
|
||||
import { SecretKeyEncoding } 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 { crypto } from "@app/lib/crypto/cryptography";
|
||||
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
||||
import { logger } from "@app/lib/logger";
|
||||
import { TAuthTokenServiceFactory } from "@app/services/auth-token/auth-token-service";
|
||||
@@ -175,7 +173,11 @@ export const userServiceFactory = ({
|
||||
const getMe = async (userId: string) => {
|
||||
const user = await userDAL.findUserEncKeyByUserId(userId);
|
||||
if (!user) throw new NotFoundError({ message: `User with ID '${userId}' not found`, name: "GetMe" });
|
||||
return user;
|
||||
|
||||
return {
|
||||
...user,
|
||||
encryptionVersion: user.encryptionVersion!
|
||||
};
|
||||
};
|
||||
|
||||
const deleteUser = async (userId: string) => {
|
||||
@@ -212,25 +214,6 @@ export const userServiceFactory = ({
|
||||
);
|
||||
};
|
||||
|
||||
const getUserPrivateKey = async (userId: string) => {
|
||||
const user = await userDAL.findUserEncKeyByUserId(userId);
|
||||
if (!user?.serverEncryptedPrivateKey || !user.serverEncryptedPrivateKeyIV || !user.serverEncryptedPrivateKeyTag) {
|
||||
throw new NotFoundError({ message: `Private key for user with ID '${userId}' not found` });
|
||||
}
|
||||
|
||||
const privateKey = crypto
|
||||
.encryption()
|
||||
.symmetric()
|
||||
.decryptWithRootEncryptionKey({
|
||||
ciphertext: user.serverEncryptedPrivateKey,
|
||||
tag: user.serverEncryptedPrivateKeyTag,
|
||||
iv: user.serverEncryptedPrivateKeyIV,
|
||||
keyEncoding: user.serverEncryptedPrivateKeyEncoding as SecretKeyEncoding
|
||||
});
|
||||
|
||||
return privateKey;
|
||||
};
|
||||
|
||||
const getUserProjectFavorites = async (userId: string, orgId: string) => {
|
||||
const orgMembership = await orgMembershipDAL.findOne({
|
||||
userId,
|
||||
@@ -311,7 +294,6 @@ export const userServiceFactory = ({
|
||||
listUserGroups,
|
||||
getUserAction,
|
||||
unlockUser,
|
||||
getUserPrivateKey,
|
||||
getAllMyAccounts,
|
||||
getUserProjectFavorites,
|
||||
removeMyDuplicateAccounts,
|
||||
|
||||
Reference in New Issue
Block a user