fix: requested changes

This commit is contained in:
Daniel Hougaard
2024-10-01 00:16:18 +04:00
parent 9c33251c44
commit b65842f5c1
8 changed files with 28 additions and 63 deletions

View File

@@ -10,6 +10,11 @@ export async function up(knex: Knex): Promise<void> {
t.string("encryptedValue").nullable().alter();
t.binary("encryptedSecret").nullable();
t.string("hashedHex").nullable().alter();
t.string("identifier", 64).nullable();
t.unique("identifier");
t.index("identifier");
});
}
}
@@ -17,11 +22,9 @@ export async function up(knex: Knex): Promise<void> {
export async function down(knex: Knex): Promise<void> {
if (await knex.schema.hasTable(TableName.SecretSharing)) {
await knex.schema.alterTable(TableName.SecretSharing, (t) => {
t.string("iv").notNullable().alter();
t.string("tag").notNullable().alter();
t.string("encryptedValue").notNullable().alter();
t.dropColumn("encryptedSecret");
t.dropColumn("identifier");
});
}
}

View File

@@ -1,23 +0,0 @@
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) => {
t.string("identifier", 36).nullable();
t.unique("identifier");
t.index("identifier");
});
}
}
export async function down(knex: Knex): Promise<void> {
if (await knex.schema.hasTable(TableName.SecretSharing)) {
await knex.schema.alterTable(TableName.SecretSharing, (t) => {
// If rolled back, all secrets created with this new structure will stop working.
t.dropColumn("identifier");
});
}
}

View File

@@ -14,7 +14,7 @@ export const SecretSharingSchema = z.object({
encryptedValue: z.string().nullable().optional(),
iv: z.string().nullable().optional(),
tag: z.string().nullable().optional(),
hashedHex: z.string(),
hashedHex: z.string().nullable().optional(),
expiresAt: z.date(),
userId: z.string().uuid().nullable().optional(),
orgId: z.string().uuid().nullable().optional(),

View File

@@ -58,7 +58,7 @@ export const registerSecretSharingRouter = async (server: FastifyZodProvider) =>
id: z.string()
}),
body: z.object({
hashedHex: z.string().min(1),
hashedHex: z.string().min(1).optional(),
password: z.string().optional()
}),
response: {

View File

@@ -72,10 +72,7 @@ export const secretSharingServiceFactory = ({
const encryptedSecret = encryptWithRoot(Buffer.from(secretValue));
// 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("base64url").substring(0, 13);
const id = crypto.randomBytes(32).toString("hex");
const hashedPassword = password ? await bcrypt.hash(password, 10) : null;
const newSharedSecret = await secretSharingDAL.create({
@@ -84,7 +81,6 @@ export const secretSharingServiceFactory = ({
tag: null,
encryptedValue: null,
encryptedSecret,
hashedHex,
name,
password: hashedPassword,
expiresAt: new Date(expiresAt),
@@ -94,7 +90,9 @@ export const secretSharingServiceFactory = ({
accessType
});
return { id: `${newSharedSecret.identifier}${hashedHex}` };
const idToReturn = `${Buffer.from(newSharedSecret.identifier!, "hex").toString("base64url")}`;
return { id: idToReturn };
};
const createPublicSharedSecret = async ({
@@ -124,8 +122,7 @@ export const secretSharingServiceFactory = ({
const encryptWithRoot = kmsService.encryptWithRootKey();
const encryptedSecret = encryptWithRoot(Buffer.from(secretValue));
const id = crypto.randomBytes(27).toString("base64url");
const hashedHex = crypto.createHash("sha256").update(id).digest("base64url").substring(0, 13);
const id = crypto.randomBytes(32).toString("hex");
const hashedPassword = password ? await bcrypt.hash(password, 10) : null;
const newSharedSecret = await secretSharingDAL.create({
@@ -133,16 +130,14 @@ export const secretSharingServiceFactory = ({
encryptedValue: null,
iv: null,
tag: null,
hashedHex,
encryptedSecret,
password: hashedPassword,
expiresAt: new Date(expiresAt),
expiresAfterViews,
accessType
});
return { id: `${newSharedSecret.identifier}${hashedHex}` };
return { id: `${Buffer.from(newSharedSecret.identifier!, "hex").toString("base64url")}` };
};
const getSharedSecrets = async ({
@@ -220,8 +215,7 @@ export const secretSharingServiceFactory = ({
hashedHex
})
: await secretSharingDAL.findOne({
hashedHex,
identifier: sharedSecretId
identifier: Buffer.from(sharedSecretId, "base64url").toString("hex")
});
if (!sharedSecret)
@@ -295,12 +289,12 @@ export const secretSharingServiceFactory = ({
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
if (!permission) throw new ForbiddenRequestError({ name: "User does not belong to the specified organization" });
const deletedSharedSecret = await secretSharingDAL.deleteById(sharedSecretId);
const sharedSecret = isUuidV4(sharedSecretId)
? await secretSharingDAL.findById(sharedSecretId)
: await secretSharingDAL.findOne({ identifier: sharedSecretId });
const deletedSharedSecret = await secretSharingDAL.deleteById(sharedSecretId);
if (sharedSecret.orgId && sharedSecret.orgId !== orgId)
throw new ForbiddenRequestError({ message: "User does not have permission to delete shared secret" });

View File

@@ -28,7 +28,7 @@ export type TCreatePublicSharedSecretDTO = {
export type TGetActiveSharedSecretByIdDTO = {
sharedSecretId: string;
hashedHex: string;
hashedHex?: string;
orgId?: string;
password?: string;
};

View File

@@ -8,7 +8,7 @@ export const secretSharingKeys = {
allSharedSecrets: () => ["sharedSecrets"] as const,
specificSharedSecrets: ({ offset, limit }: { offset: number; limit: number }) =>
[...secretSharingKeys.allSharedSecrets(), { offset, limit }] as const,
getSecretById: (arg: { id: string; hashedHex: string; password?: string }) => [
getSecretById: (arg: { id: string; hashedHex: string | null; password?: string }) => [
"shared-secret",
arg
]
@@ -46,7 +46,7 @@ export const useGetActiveSharedSecretById = ({
password
}: {
sharedSecretId: string;
hashedHex: string;
hashedHex: string | null;
password?: string;
}) => {
return useQuery<TViewSharedSecretResponse>(
@@ -55,7 +55,7 @@ export const useGetActiveSharedSecretById = ({
const { data } = await apiRequest.post<TViewSharedSecretResponse>(
`/api/v1/secret-sharing/public/${sharedSecretId}`,
{
hashedHex,
...(hashedHex && { hashedHex }),
password
}
);
@@ -63,7 +63,7 @@ export const useGetActiveSharedSecretById = ({
return data;
},
{
enabled: Boolean(sharedSecretId) && Boolean(hashedHex)
enabled: Boolean(sharedSecretId)
}
);
};

View File

@@ -15,14 +15,6 @@ const extractDetailsFromUrl = (router: NextRouter) => {
const idString = id as string;
if (!idString) {
return {
id: "",
hashedHex: "",
key: null
};
}
if (urlEncodedKey) {
const [hashedHex, key] = urlEncodedKey ? urlEncodedKey.toString().split("-") : ["", ""];
@@ -33,13 +25,9 @@ const extractDetailsFromUrl = (router: NextRouter) => {
};
}
// get the first 36 characters as id and the rest as hex
const idPart = idString.substring(0, 36);
const hexPart = idString.substring(36);
return {
id: idPart || "",
hashedHex: hexPart || "",
id: idString,
hashedHex: null,
key: null
};
};
@@ -65,6 +53,9 @@ export const ViewSecretPublicPage = () => {
((error as AxiosError)?.response?.data as { message: string })?.message ===
"Invalid credentials";
console.log("data", fetchSecret);
console.log("err", error);
const shouldShowPasswordPrompt =
isInvalidCredential || (fetchSecret?.isPasswordProtected && !fetchSecret.secret);
const isValidatingPassword = Boolean(password) && isFetching;