requested changes

This commit is contained in:
Daniel Hougaard
2024-09-30 22:51:02 +04:00
parent 1a2495a95c
commit fa63c150dd
8 changed files with 116 additions and 24 deletions
@@ -0,0 +1,68 @@
/* eslint-disable @typescript-eslint/ban-ts-comment */
import { Knex } from "knex";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
if (await knex.schema.hasTable(TableName.SecretSharing)) {
await knex.schema.alterTable(TableName.SecretSharing, (t) => {
// Add a new column
t.string("new_id", 36).nullable();
});
// Copy data from old column to new column
await knex(TableName.SecretSharing).update({
// @ts-ignore
new_id: knex.raw("id::text")
});
await knex.schema.alterTable(TableName.SecretSharing, (t) => {
// Make the new column not nullable
t.string("new_id", 36).notNullable().alter();
// Drop the old primary key
t.dropPrimary();
// Drop the old id column
t.dropColumn("id");
// Rename the new column to 'id'
t.renameColumn("new_id", "id");
// Set the new column as primary key
t.primary(["id"]);
});
}
}
export async function down(knex: Knex): Promise<void> {
if (await knex.schema.hasTable(TableName.SecretSharing)) {
await knex.schema.alterTable(TableName.SecretSharing, (t) => {
// Add a new UUID column
t.uuid("new_id").nullable();
});
// Copy data from string id to UUID, ensuring valid UUID format
await knex(TableName.SecretSharing).update({
// @ts-ignore
new_id: knex.raw("id::uuid")
});
await knex.schema.alterTable(TableName.SecretSharing, (t) => {
// Make the new column not nullable
t.uuid("new_id").notNullable().alter();
// Drop the old primary key
t.dropPrimary();
// Drop the old id column
t.dropColumn("id");
// Rename the new column to 'id'
t.renameColumn("new_id", "id");
// Set the new column as primary key
t.primary(["id"]);
});
}
}
+2 -2
View File
@@ -10,7 +10,6 @@ import { zodBuffer } from "@app/lib/zod";
import { TImmutableDBKeys } from "./models"; import { TImmutableDBKeys } from "./models";
export const SecretSharingSchema = z.object({ export const SecretSharingSchema = z.object({
id: z.string().uuid(),
encryptedValue: z.string().nullable().optional(), encryptedValue: z.string().nullable().optional(),
iv: z.string().nullable().optional(), iv: z.string().nullable().optional(),
tag: z.string().nullable().optional(), tag: z.string().nullable().optional(),
@@ -25,7 +24,8 @@ export const SecretSharingSchema = z.object({
name: z.string().nullable().optional(), name: z.string().nullable().optional(),
lastViewedAt: z.date().nullable().optional(), lastViewedAt: z.date().nullable().optional(),
password: z.string().nullable().optional(), password: z.string().nullable().optional(),
encryptedSecret: zodBuffer.nullable().optional() encryptedSecret: zodBuffer.nullable().optional(),
id: z.string()
}); });
export type TSecretSharing = z.infer<typeof SecretSharingSchema>; export type TSecretSharing = z.infer<typeof SecretSharingSchema>;
@@ -55,7 +55,7 @@ export const registerSecretSharingRouter = async (server: FastifyZodProvider) =>
}, },
schema: { schema: {
params: z.object({ params: z.object({
id: z.string().uuid() id: z.string()
}), }),
body: z.object({ body: z.object({
hashedHex: z.string().min(1), hashedHex: z.string().min(1),
@@ -107,8 +107,7 @@ export const registerSecretSharingRouter = async (server: FastifyZodProvider) =>
}), }),
response: { response: {
200: z.object({ 200: z.object({
id: z.string().uuid(), id: z.string()
hashedHex: z.string()
}) })
} }
}, },
@@ -117,7 +116,7 @@ export const registerSecretSharingRouter = async (server: FastifyZodProvider) =>
...req.body, ...req.body,
accessType: SecretSharingAccessType.Anyone accessType: SecretSharingAccessType.Anyone
}); });
return { id: sharedSecret.id, hashedHex: sharedSecret.hashedHex }; return { id: sharedSecret.id };
} }
}); });
@@ -138,8 +137,7 @@ export const registerSecretSharingRouter = async (server: FastifyZodProvider) =>
}), }),
response: { response: {
200: z.object({ 200: z.object({
id: z.string().uuid(), id: z.string()
hashedHex: z.string()
}) })
} }
}, },
@@ -153,7 +151,7 @@ export const registerSecretSharingRouter = async (server: FastifyZodProvider) =>
actorOrgId: req.permission.orgId, actorOrgId: req.permission.orgId,
...req.body ...req.body
}); });
return { id: sharedSecret.id, hashedHex: sharedSecret.hashedHex }; return { id: sharedSecret.id };
} }
}); });
@@ -165,7 +163,7 @@ export const registerSecretSharingRouter = async (server: FastifyZodProvider) =>
}, },
schema: { schema: {
params: z.object({ params: z.object({
sharedSecretId: z.string().uuid() sharedSecretId: z.string()
}), }),
response: { response: {
200: SecretSharingSchema 200: SecretSharingSchema
@@ -82,11 +82,21 @@ export const secretSharingDALFactory = (db: TDbClient) => {
} }
}; };
const create = async (data: Omit<TSecretSharing, "createdAt" | "updatedAt">, tx?: Knex) => {
try {
const [res] = await (tx || db)(TableName.SecretSharing).insert(data).returning("*");
return res;
} catch (error) {
throw new DatabaseError({ error, name: "Create Shared Secret" });
}
};
return { return {
...sharedSecretOrm, ...sharedSecretOrm,
countAllUserOrgSharedSecrets, countAllUserOrgSharedSecrets,
pruneExpiredSharedSecrets, pruneExpiredSharedSecrets,
softDeleteById, softDeleteById,
findActiveSharedSecrets findActiveSharedSecrets,
create
}; };
}; };
@@ -68,10 +68,15 @@ export const secretSharingServiceFactory = ({
const encryptWithRoot = kmsService.encryptWithRootKey(); const encryptWithRoot = kmsService.encryptWithRootKey();
const encryptedSecret = encryptWithRoot(Buffer.from(secretValue)); const encryptedSecret = encryptWithRoot(Buffer.from(secretValue));
const hashedHex = crypto.createHash("sha256").update(secretValue).digest("hex").substring(0, 13);
// This will be 36 characters long, due to encoding it to base64.
const id = crypto.randomBytes(27).toString("base64url");
const hashedHex = crypto.createHash("sha256").update(id).digest("base64").substring(0, 13);
const hashedPassword = password ? await bcrypt.hash(password, 10) : null; const hashedPassword = password ? await bcrypt.hash(password, 10) : null;
const newSharedSecret = await secretSharingDAL.create({ const newSharedSecret = await secretSharingDAL.create({
id,
iv: null, iv: null,
tag: null, tag: null,
encryptedValue: null, encryptedValue: null,
@@ -86,7 +91,7 @@ export const secretSharingServiceFactory = ({
accessType accessType
}); });
return { id: newSharedSecret.id, hashedHex: newSharedSecret.hashedHex }; return { id: `${newSharedSecret.id}${hashedHex}` };
}; };
const createPublicSharedSecret = async ({ const createPublicSharedSecret = async ({
@@ -116,10 +121,12 @@ export const secretSharingServiceFactory = ({
const encryptWithRoot = kmsService.encryptWithRootKey(); const encryptWithRoot = kmsService.encryptWithRootKey();
const encryptedSecret = encryptWithRoot(Buffer.from(secretValue)); const encryptedSecret = encryptWithRoot(Buffer.from(secretValue));
const hashedHex = crypto.createHash("sha256").update(secretValue).digest("hex").substring(0, 13); const id = crypto.randomBytes(27).toString("base64url");
const hashedHex = crypto.createHash("sha256").update(id).digest("hex").substring(0, 13);
const hashedPassword = password ? await bcrypt.hash(password, 10) : null; const hashedPassword = password ? await bcrypt.hash(password, 10) : null;
const newSharedSecret = await secretSharingDAL.create({ const newSharedSecret = await secretSharingDAL.create({
id,
encryptedValue: null, encryptedValue: null,
iv: null, iv: null,
tag: null, tag: null,
@@ -132,7 +139,7 @@ export const secretSharingServiceFactory = ({
accessType accessType
}); });
return { id: newSharedSecret.id, hashedHex: newSharedSecret.hashedHex }; return { id: `${newSharedSecret.id}${hashedHex}` };
}; };
const getSharedSecrets = async ({ const getSharedSecrets = async ({
@@ -15,7 +15,6 @@ export type TSharedSecret = {
export type TCreatedSharedSecret = { export type TCreatedSharedSecret = {
id: string; id: string;
hashedHex: string;
}; };
export type TCreateSharedSecretRequest = { export type TCreateSharedSecretRequest = {
@@ -76,7 +76,7 @@ export const ShareSecretForm = ({ isPublic, value }: Props) => {
try { try {
const expiresAt = new Date(new Date().getTime() + Number(expiresIn)); const expiresAt = new Date(new Date().getTime() + Number(expiresIn));
const { id, hashedHex } = await createSharedSecret.mutateAsync({ const { id } = await createSharedSecret.mutateAsync({
name, name,
password, password,
secretValue: secret, secretValue: secret,
@@ -85,7 +85,7 @@ export const ShareSecretForm = ({ isPublic, value }: Props) => {
accessType accessType
}); });
setSecretLink(`${window.location.origin}/shared/secret/${id}-${hashedHex}`); setSecretLink(`${window.location.origin}/shared/secret/${id}`);
reset(); reset();
setCopyTextSecret("secret"); setCopyTextSecret("secret");
@@ -13,23 +13,33 @@ import { PasswordContainer, SecretContainer, SecretErrorContainer } from "./comp
const extractDetailsFromUrl = (router: NextRouter) => { const extractDetailsFromUrl = (router: NextRouter) => {
const { id, key: urlEncodedKey } = router.query; const { id, key: urlEncodedKey } = router.query;
const idString = id as string;
if (!idString) {
return {
id: "",
hashedHex: "",
key: null
};
}
if (urlEncodedKey) { if (urlEncodedKey) {
const [hashedHex, key] = urlEncodedKey ? urlEncodedKey.toString().split("-") : ["", ""]; const [hashedHex, key] = urlEncodedKey ? urlEncodedKey.toString().split("-") : ["", ""];
return { return {
id: id as string, id: idString,
hashedHex, hashedHex,
key key
}; };
} }
// its like this {uuid}-{hex} so example: idpart1-idpart2-idpart3-idpart4-hex // get the first 36 characters as id and the rest as hex
const extractedId = id?.toString().split("-").slice(0, 5).join("-"); const idPart = idString.substring(0, 36);
const extractedHex = id?.toString().split("-").slice(5).join("-"); const hexPart = idString.substring(36);
return { return {
id: extractedId || "", id: idPart || "",
hashedHex: extractedHex || "", hashedHex: hexPart || "",
key: null key: null
}; };
}; };