More RecipeService security and tests.
This commit is contained in:
		
							parent
							
								
									d3a532fb12
								
							
						
					
					
						commit
						35ef2aa039
					
				@ -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));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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())) {
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user