More RecipeService security and tests.

This commit is contained in:
JesseBrault0709 2024-07-09 09:24:36 +02:00
parent d3a532fb12
commit 35ef2aa039
5 changed files with 77 additions and 23 deletions

View File

@ -64,7 +64,7 @@ public class RecipeServiceTests {
public void getByIdPublic() throws RecipeException {
final User owner = this.createTestUser("recipeOwner");
Recipe recipe = this.createTestRecipe(owner);
recipe = this.recipeService.setPublic(recipe, true);
recipe = this.recipeService.setPublic(recipe, owner, true);
final Recipe byId = this.recipeService.getById(recipe.getId());
assertThat(byId.getId(), is(recipe.getId()));
assertThat(byId.getTitle(), is("My Recipe"));
@ -93,7 +93,7 @@ public class RecipeServiceTests {
public void getByIdOkayWhenPublic() {
final User owner = this.createTestUser("recipeOwner");
final Recipe notYetPublicRecipe = this.createTestRecipe(owner);
final Recipe publicRecipe = this.recipeService.setPublic(notYetPublicRecipe, true);
final Recipe publicRecipe = this.recipeService.setPublic(notYetPublicRecipe, owner, true);
assertDoesNotThrow(() -> this.recipeService.getById(publicRecipe.getId()));
}
@ -103,7 +103,7 @@ public class RecipeServiceTests {
final User owner = this.createTestUser("recipeOwner");
final User viewer = this.createTestUser("viewer");
final Recipe notYetPublicRecipe = this.createTestRecipe(owner);
final Recipe publicRecipe = this.recipeService.setPublic(notYetPublicRecipe, true);
final Recipe publicRecipe = this.recipeService.setPublic(notYetPublicRecipe, owner, true);
assertDoesNotThrow(() -> this.recipeService.getById(publicRecipe.getId(), viewer));
}
@ -112,7 +112,7 @@ public class RecipeServiceTests {
public void getByIdWithStarsPublic() throws RecipeException {
final User owner = this.createTestUser("recipeOwner");
Recipe recipe = this.createTestRecipe(owner);
recipe = this.recipeService.setPublic(recipe, true);
recipe = this.recipeService.setPublic(recipe, owner, true);
final RecipeStar star = this.recipeService.addStar(recipe, owner);
final Recipe byIdWithStars = this.recipeService.getByIdWithStars(recipe.getId());
assertThat(byIdWithStars.getStars(), containsStars(star));
@ -132,7 +132,7 @@ public class RecipeServiceTests {
public void getByIdWithStarsOkayWhenPublic() {
final User owner = this.createTestUser("recipeOwner");
final Recipe notYetPublicRecipe = this.createTestRecipe(owner);
final Recipe publicRecipe = this.recipeService.setPublic(notYetPublicRecipe, true);
final Recipe publicRecipe = this.recipeService.setPublic(notYetPublicRecipe, owner, true);
assertDoesNotThrow(() -> this.recipeService.getByIdWithStars(publicRecipe.getId()));
}
@ -142,7 +142,7 @@ public class RecipeServiceTests {
final User owner = this.createTestUser("recipeOwner");
final User viewer = this.createTestUser("viewer");
final Recipe notYetPublicRecipe = this.createTestRecipe(owner);
final Recipe publicRecipe = this.recipeService.setPublic(notYetPublicRecipe, true);
final Recipe publicRecipe = this.recipeService.setPublic(notYetPublicRecipe, owner, true);
assertDoesNotThrow(() -> this.recipeService.getByIdWithStars(publicRecipe.getId(), viewer));
}
@ -157,9 +157,9 @@ public class RecipeServiceTests {
Recipe r1 = this.createTestRecipe(owner);
Recipe r2 = this.createTestRecipe(owner);
r0 = this.recipeService.setPublic(r0, true);
r1 = this.recipeService.setPublic(r1, true);
r2 = this.recipeService.setPublic(r2, true);
r0 = this.recipeService.setPublic(r0, owner, true);
r1 = this.recipeService.setPublic(r1, owner, true);
r2 = this.recipeService.setPublic(r2, owner, true);
// r0.stars = 0, r1.stars = 1, r2.stars = 2
this.recipeService.addStar(r1, u0);
@ -236,8 +236,8 @@ public class RecipeServiceTests {
Recipe r0 = this.createTestRecipe(owner);
Recipe r1 = this.createTestRecipe(owner);
r0 = this.recipeService.setPublic(r0, true);
r1 = this.recipeService.setPublic(r1, true);
r0 = this.recipeService.setPublic(r0, owner, true);
r1 = this.recipeService.setPublic(r1, owner, true);
final List<Recipe> publicRecipes = this.recipeService.getPublicRecipes();
assertThat(publicRecipes.size(), is(2));
@ -274,10 +274,19 @@ public class RecipeServiceTests {
final Recipe recipe = this.recipeService.create(
owner, "My Recipe", "# A Heading"
);
final String rendered = this.recipeService.getRenderedMarkdown(recipe);
final String rendered = this.recipeService.getRenderedMarkdown(recipe, owner);
assertThat(rendered, is("<h1>A Heading</h1>"));
}
@Test
@DirtiesContext
public void getRenderedMarkThrowsIfNotViewable() {
final User owner = this.createTestUser("recipeOwner");
final User notViewer = this.createTestUser("notViewer");
final Recipe recipe = this.createTestRecipe(owner);
assertThrows(AccessDeniedException.class, () -> this.recipeService.getRenderedMarkdown(recipe, notViewer));
}
@Test
@DirtiesContext
public void updateRawText() {
@ -286,10 +295,22 @@ public class RecipeServiceTests {
owner, "My Recipe", "# A Heading"
);
final String newRawText = "# A Heading\n## A Subheading";
recipe = this.recipeService.updateRawText(recipe, newRawText);
recipe = this.recipeService.updateRawText(recipe, owner, newRawText);
assertThat(recipe.getRawText(), is(newRawText));
}
@Test
@DirtiesContext
public void updateRawTextThrowsIfNotOwner() {
final User owner = this.createTestUser("recipeOwner");
final User notOwner = this.createTestUser("notOwner");
final Recipe recipe = this.createTestRecipe(owner);
assertThrows(
AccessDeniedException.class,
() -> this.recipeService.updateRawText(recipe, notOwner, "should fail")
);
}
@Test
@DirtiesContext
public void updateOwnerViaUser() throws RecipeException {
@ -343,4 +364,22 @@ public class RecipeServiceTests {
assertThat(recipe.getStars(), is(empty()));
}
@Test
@DirtiesContext
public void deleteRecipe() {
final User owner = this.createTestUser("recipeOwner");
final Recipe toDelete = this.createTestRecipe(owner);
this.recipeService.deleteRecipe(toDelete, owner);
assertThrows(RecipeException.class, () -> this.recipeService.getById(toDelete.getId(), owner));
}
@Test
@DirtiesContext
public void deleteRecipeThrowsIfNotOwner() {
final User owner = this.createTestUser("recipeOwner");
final User notOwner = this.createTestUser("notOwner");
final Recipe toDelete = this.createTestRecipe(owner);
assertThrows(AccessDeniedException.class, () -> this.recipeService.deleteRecipe(toDelete, notOwner));
}
}

View File

@ -4,5 +4,6 @@ import app.mealsmadeeasy.api.user.User;
public interface RecipeSecurity {
boolean isOwner(Recipe recipe, User user);
boolean isOwner(long recipeId, User user) throws RecipeException;
boolean isViewableBy(Recipe recipe, User user);
}

View File

@ -19,6 +19,15 @@ public class RecipeSecurityImpl implements RecipeSecurity {
return recipe.getOwner() != null && recipe.getOwner().getId().equals(user.getId());
}
@Override
public boolean isOwner(long recipeId, User user) throws RecipeException {
final Recipe recipe = this.recipeRepository.findById(recipeId).orElseThrow(() -> new RecipeException(
RecipeException.Type.INVALID_ID,
"No such Recipe with id " + recipeId
));
return this.isOwner(recipe, user);
}
@Override
public boolean isViewableBy(Recipe recipe, User user) {
if (Objects.equals(recipe.getOwner().getId(), user.getId())) {

View File

@ -24,9 +24,9 @@ public interface RecipeService {
List<Recipe> getRecipesViewableBy(User user);
List<Recipe> getRecipesOwnedBy(User user);
String getRenderedMarkdown(Recipe recipe);
String getRenderedMarkdown(Recipe recipe, User viewer);
Recipe updateRawText(Recipe recipe, String newRawText);
Recipe updateRawText(Recipe recipe, User owner, String newRawText);
Recipe updateOwner(Recipe recipe, User oldOwner, User newOwner) throws RecipeException;
@ -34,7 +34,7 @@ public interface RecipeService {
void deleteStarByUser(Recipe recipe, User giver) throws RecipeException;
void deleteStar(RecipeStar recipeStar);
Recipe setPublic(Recipe recipe, boolean isPublic);
Recipe setPublic(Recipe recipe, User owner, boolean isPublic);
Recipe addViewer(Recipe recipe, User user);
Recipe removeViewer(Recipe recipe, User user);
@ -47,7 +47,7 @@ public interface RecipeService {
void deleteComment(RecipeComment comment);
Recipe clearComments(Recipe recipe);
void deleteRecipe(Recipe recipe);
void deleteById(long id);
void deleteRecipe(Recipe recipe, User owner);
void deleteById(long id, User owner);
}

View File

@ -136,7 +136,8 @@ public class RecipeServiceImpl implements RecipeService {
}
@Override
public String getRenderedMarkdown(Recipe recipe) {
@PreAuthorize("#recipe.isPublic || @recipeSecurity.isViewableBy(#recipe, #viewer)")
public String getRenderedMarkdown(Recipe recipe, User viewer) {
RecipeEntity entity = (RecipeEntity) recipe;
if (entity.getCachedRenderedText() == null) {
entity.setCachedRenderedText(renderAndCleanMarkdown(entity.getRawText()));
@ -146,7 +147,8 @@ public class RecipeServiceImpl implements RecipeService {
}
@Override
public Recipe updateRawText(Recipe recipe, String newRawText) {
@PreAuthorize("@recipeSecurity.isOwner(#recipe, #owner)")
public Recipe updateRawText(Recipe recipe, User owner, String newRawText) {
final RecipeEntity entity = (RecipeEntity) recipe;
entity.setCachedRenderedText(null);
entity.setRawText(newRawText);
@ -188,7 +190,8 @@ public class RecipeServiceImpl implements RecipeService {
}
@Override
public Recipe setPublic(Recipe recipe, boolean isPublic) {
@PreAuthorize("@recipeSecurity.isOwner(#recipe, #owner)")
public Recipe setPublic(Recipe recipe, User owner, boolean isPublic) {
final RecipeEntity entity = (RecipeEntity) recipe;
entity.setPublic(isPublic);
return this.recipeRepository.save(entity);
@ -266,12 +269,14 @@ public class RecipeServiceImpl implements RecipeService {
}
@Override
public void deleteRecipe(Recipe recipe) {
@PreAuthorize("@recipeSecurity.isOwner(#recipe, #owner)")
public void deleteRecipe(Recipe recipe, User owner) {
this.recipeRepository.delete((RecipeEntity) recipe);
}
@Override
public void deleteById(long id) {
@PreAuthorize("@recipeSecurity.isOwner(#id, #owner)")
public void deleteById(long id, User owner) {
this.recipeRepository.deleteById(id);
}