1
0
Fork 0
mirror of https://github.com/mealie-recipes/mealie.git synced 2025-07-24 07:39:41 +02:00

feat: Additional Household Permissions (#4158)

Co-authored-by: Kuchenpirat <24235032+Kuchenpirat@users.noreply.github.com>
This commit is contained in:
Michael Genson 2024-09-17 10:48:14 -05:00 committed by GitHub
parent b1820f9b23
commit fd0257c1b8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
37 changed files with 690 additions and 185 deletions

View file

@ -1,5 +1,7 @@
import { describe, test, expect } from "vitest";
import { ref, Ref } from "@nuxtjs/composition-api";
import { useRecipePermissions } from "./use-recipe-permissions";
import { HouseholdSummary } from "~/lib/api/types/household";
import { Recipe } from "~/lib/api/types/recipe";
import { UserOut } from "~/lib/api/types/user";
@ -32,35 +34,76 @@ describe("test use recipe permissions", () => {
...overrides,
});
const createRecipeHousehold = (overrides: Partial<HouseholdSummary>, lockRecipeEdits = false): Ref<HouseholdSummary> => (
ref({
id: commonHouseholdId,
groupId: commonGroupId,
name: "My Household",
slug: "my-household",
preferences: {
id: "my-household-preferences-id",
lockRecipeEditsFromOtherHouseholds: lockRecipeEdits,
},
...overrides,
})
);
test("when user is null, cannot edit", () => {
const result = useRecipePermissions(createRecipe({}), null);
const result = useRecipePermissions(createRecipe({}), createRecipeHousehold({}), null);
expect(result.canEditRecipe.value).toBe(false);
});
test("when user is recipe owner, can edit", () => {
const result = useRecipePermissions(createRecipe({}), createUser({}));
const result = useRecipePermissions(createRecipe({}), ref(), createUser({}));
expect(result.canEditRecipe.value).toBe(true);
});
test("when user is not recipe owner, is correct group and household, and recipe is unlocked, can edit", () => {
const result = useRecipePermissions(
createRecipe({}),
createUser({ id: "other-user-id" }),
);
expect(result.canEditRecipe.value).toBe(true);
});
test(
"when user is not recipe owner, is correct group and household, recipe is unlocked, and household is unlocked, can edit",
() => {
const result = useRecipePermissions(
createRecipe({}),
createRecipeHousehold({}),
createUser({ id: "other-user-id" }),
);
expect(result.canEditRecipe.value).toBe(true);
}
);
test(
"when user is not recipe owner, is correct group and household, recipe is unlocked, but household is locked, can edit",
() => {
const result = useRecipePermissions(
createRecipe({}),
createRecipeHousehold({}, true),
createUser({ id: "other-user-id" }),
);
expect(result.canEditRecipe.value).toBe(true);
}
);
test("when user is not recipe owner, and user is other group, cannot edit", () => {
const result = useRecipePermissions(
createRecipe({}),
createRecipeHousehold({}),
createUser({ id: "other-user-id", groupId: "other-group-id"}),
);
expect(result.canEditRecipe.value).toBe(false);
});
test("when user is not recipe owner, and user is other household, cannot edit", () => {
test("when user is not recipe owner, and user is other household, and household is unlocked, can edit", () => {
const result = useRecipePermissions(
createRecipe({}),
createRecipeHousehold({}),
createUser({ id: "other-user-id", householdId: "other-household-id" }),
);
expect(result.canEditRecipe.value).toBe(true);
});
test("when user is not recipe owner, and user is other household, and household is locked, cannot edit", () => {
const result = useRecipePermissions(
createRecipe({}),
createRecipeHousehold({}, true),
createUser({ id: "other-user-id", householdId: "other-household-id" }),
);
expect(result.canEditRecipe.value).toBe(false);
@ -69,13 +112,14 @@ describe("test use recipe permissions", () => {
test("when user is not recipe owner, and recipe is locked, cannot edit", () => {
const result = useRecipePermissions(
createRecipe({}, true),
createRecipeHousehold({}),
createUser({ id: "other-user-id"}),
);
expect(result.canEditRecipe.value).toBe(false);
});
test("when user is recipe owner, and recipe is locked, can edit", () => {
const result = useRecipePermissions(createRecipe({}, true), createUser({}));
test("when user is recipe owner, and recipe is locked, and household is locked, can edit", () => {
const result = useRecipePermissions(createRecipe({}, true), createRecipeHousehold({}, true), createUser({}));
expect(result.canEditRecipe.value).toBe(true);
});
});

View file

@ -1,34 +1,44 @@
import { computed } from "@nuxtjs/composition-api";
import { computed, Ref } from "@nuxtjs/composition-api";
import { Recipe } from "~/lib/api/types/recipe";
import { HouseholdSummary } from "~/lib/api/types/household";
import { UserOut } from "~/lib/api/types/user";
export function useRecipePermissions(recipe: Recipe, user: UserOut | null) {
const canEditRecipe = computed(() => {
// Check recipe owner
if (!user?.id) {
return false;
}
if (user.id === recipe.userId) {
return true;
}
// Check group and household
if (user.groupId !== recipe.groupId) {
return false;
}
if (user.householdId !== recipe.householdId) {
return false;
}
// Check recipe
if (recipe.settings?.locked) {
return false;
}
return true;
});
return {
canEditRecipe,
export function useRecipePermissions(
recipe: Recipe,
recipeHousehold: Ref<HouseholdSummary | undefined>,
user: UserOut | null,
) {
const canEditRecipe = computed(() => {
// Check recipe owner
if (!user?.id) {
return false;
}
if (user.id === recipe.userId) {
return true;
}
// Check group and household
if (user.groupId !== recipe.groupId) {
return false;
}
if (user.householdId !== recipe.householdId) {
if (!recipeHousehold.value?.preferences) {
return false;
}
if (recipeHousehold.value?.preferences.lockRecipeEditsFromOtherHouseholds) {
return false;
}
}
// Check recipe
if (recipe.settings?.locked) {
return false;
}
return true;
});
return {
canEditRecipe,
}
}

View file

@ -36,6 +36,8 @@ export const useHouseholdSelf = function () {
if (data) {
householdSelfRef.value.preferences = data;
}
return data || undefined;
},
};

View file

@ -65,6 +65,12 @@ export const useUserForm = () => {
type: fieldTypes.BOOLEAN,
rules: ["required"],
},
{
label: i18n.tc("user.user-can-manage-household"),
varName: "canManageHousehold",
type: fieldTypes.BOOLEAN,
rules: ["required"],
},
{
label: i18n.tc("user.enable-advanced-features"),
varName: "advanced",