Compare commits
No commits in common. "6eead311934f09a8cbbd8d7154478889c6bc9c2e" and "2fbb9ee51e2b8ce349db68a378bcae6696f0ec19" have entirely different histories.
6eead31193
...
2fbb9ee51e
@ -362,33 +362,6 @@ public class RecipeServiceTests {
|
|||||||
assertThrows(AccessDeniedException.class, () -> this.recipeService.deleteRecipe(toDelete.getId(), notOwner));
|
assertThrows(AccessDeniedException.class, () -> this.recipeService.deleteRecipe(toDelete.getId(), notOwner));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void deleteRecipeByUsernameAndSlug() {
|
|
||||||
final User owner = this.seedUser();
|
|
||||||
final Recipe toDelete = this.createTestRecipe(owner);
|
|
||||||
this.recipeService.deleteRecipe(owner.getUsername(), toDelete.getSlug(), owner);
|
|
||||||
assertThrows(NoSuchEntityWithIdException.class, () -> this.recipeService.getById(toDelete.getId(), owner));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void deleteRecipeByUsernameAndSlug_throwsIfNullDeleter() {
|
|
||||||
final User owner = this.seedUser();
|
|
||||||
final Recipe recipe = this.createTestRecipe(owner);
|
|
||||||
assertThrows(AccessDeniedException.class, () -> this.recipeService.deleteRecipe(
|
|
||||||
owner.getUsername(), recipe.getSlug(), null
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void deleteRecipeByUsernameAndSlug_throwsIfNotOwner() {
|
|
||||||
final User owner = this.seedUser();
|
|
||||||
final User wrong = this.seedUser();
|
|
||||||
final Recipe recipe = this.createTestRecipe(owner);
|
|
||||||
assertThrows(AccessDeniedException.class, () -> this.recipeService.deleteRecipe(
|
|
||||||
owner.getUsername(), recipe.getSlug(), wrong
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void createDraftReturnsDefaultRecipeDraft() {
|
public void createDraftReturnsDefaultRecipeDraft() {
|
||||||
final User owner = this.seedUser();
|
final User owner = this.seedUser();
|
||||||
|
|||||||
@ -328,59 +328,4 @@ public class RecipesControllerTests {
|
|||||||
.andExpect(status().isNoContent());
|
.andExpect(status().isNoContent());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void deleteRecipeByOwner_returnsNoContent() throws Exception {
|
|
||||||
final User owner = this.seedUser();
|
|
||||||
final Recipe recipe = this.createTestRecipe(owner, false);
|
|
||||||
this.mockMvc.perform(
|
|
||||||
delete("/recipes/{username}/{slug}", recipe.getOwner().getUsername(), recipe.getSlug())
|
|
||||||
.header("Authorization", "Bearer " + this.getAccessToken(owner))
|
|
||||||
)
|
|
||||||
.andExpect(status().isNoContent());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void deletePrivateRecipe_noAuth_returnsUnauthorized() throws Exception {
|
|
||||||
final User owner = this.seedUser();
|
|
||||||
final Recipe recipe = this.createTestRecipe(owner, false);
|
|
||||||
this.mockMvc.perform(
|
|
||||||
delete("/recipes/{username}/{slug}", recipe.getOwner().getUsername(), recipe.getSlug())
|
|
||||||
)
|
|
||||||
.andExpect(status().isUnauthorized());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void deletePublicRecipe_noAuth_returnsUnauthorized() throws Exception {
|
|
||||||
final User owner = this.seedUser();
|
|
||||||
final Recipe recipe = this.createTestRecipe(owner, true);
|
|
||||||
this.mockMvc.perform(
|
|
||||||
delete("/recipes/{username}/{slug}", recipe.getOwner().getUsername(), recipe.getSlug())
|
|
||||||
)
|
|
||||||
.andExpect(status().isUnauthorized());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void deletePublicRecipe_wrongAuth_returnsForbidden() throws Exception {
|
|
||||||
final User owner = this.seedUser();
|
|
||||||
final User wrong = this.seedUser();
|
|
||||||
final Recipe recipe = this.createTestRecipe(owner, true);
|
|
||||||
this.mockMvc.perform(
|
|
||||||
delete("/recipes/{username}/{slug}", recipe.getOwner().getUsername(), recipe.getSlug())
|
|
||||||
.header("Authorization", "Bearer " + this.getAccessToken(wrong))
|
|
||||||
)
|
|
||||||
.andExpect(status().isForbidden());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void deletePrivateRecipe_wrongAuth_returnsForbidden() throws Exception {
|
|
||||||
final User owner = this.seedUser();
|
|
||||||
final User wrong = this.seedUser();
|
|
||||||
final Recipe recipe = this.createTestRecipe(owner, false);
|
|
||||||
this.mockMvc.perform(
|
|
||||||
delete("/recipes/{username}/{slug}", recipe.getOwner().getUsername(), recipe.getSlug())
|
|
||||||
.header("Authorization", "Bearer " + this.getAccessToken(wrong))
|
|
||||||
)
|
|
||||||
.andExpect(status().isForbidden());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -52,10 +52,11 @@ public class Recipe {
|
|||||||
@JoinColumn(name = "owner_id", nullable = false)
|
@JoinColumn(name = "owner_id", nullable = false)
|
||||||
private User owner;
|
private User owner;
|
||||||
|
|
||||||
@OneToMany(mappedBy = "recipe", orphanRemoval = true, cascade = CascadeType.ALL)
|
@OneToMany
|
||||||
|
@JoinColumn(name = "recipe_id")
|
||||||
private Set<RecipeStar> stars = new HashSet<>();
|
private Set<RecipeStar> stars = new HashSet<>();
|
||||||
|
|
||||||
@OneToMany(mappedBy = "recipe", orphanRemoval = true, cascade = CascadeType.ALL)
|
@OneToMany(mappedBy = "recipe")
|
||||||
private Set<RecipeComment> comments = new HashSet<>();
|
private Set<RecipeComment> comments = new HashSet<>();
|
||||||
|
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
|
|||||||
@ -74,6 +74,4 @@ public interface RecipeRepository extends JpaRepository<Recipe, Integer> {
|
|||||||
@Query("SELECT count(r) FROM Recipe r WHERE ?1 MEMBER OF r.viewers OR r.isPublic IS TRUE")
|
@Query("SELECT count(r) FROM Recipe r WHERE ?1 MEMBER OF r.viewers OR r.isPublic IS TRUE")
|
||||||
int countViewableBy(User viewer);
|
int countViewableBy(User viewer);
|
||||||
|
|
||||||
void deleteRecipeByOwnerUsernameAndSlug(String username, String slug);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,12 +17,11 @@ public class RecipeSecurity {
|
|||||||
this.recipeRepository = recipeRepository;
|
this.recipeRepository = recipeRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isOwner(Recipe recipe, @Nullable User user) {
|
public boolean isOwner(Recipe recipe, User user) {
|
||||||
if (user == null) return false;
|
|
||||||
return recipe.getOwner() != null && recipe.getOwner().getId().equals(user.getId());
|
return recipe.getOwner() != null && recipe.getOwner().getId().equals(user.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isOwner(Integer recipeId, @Nullable User user) {
|
public boolean isOwner(Integer recipeId, User user) {
|
||||||
final Recipe recipe = this.recipeRepository.findById(recipeId).orElseThrow(
|
final Recipe recipe = this.recipeRepository.findById(recipeId).orElseThrow(
|
||||||
() -> new NoSuchEntityWithIdException(Recipe.class, recipeId)
|
() -> new NoSuchEntityWithIdException(Recipe.class, recipeId)
|
||||||
);
|
);
|
||||||
|
|||||||
@ -219,12 +219,6 @@ public class RecipeService {
|
|||||||
this.recipeRepository.deleteById(id);
|
this.recipeRepository.deleteById(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PreAuthorize("@recipeSecurity.isOwner(#username, #slug, #deleter)")
|
|
||||||
@Transactional
|
|
||||||
public void deleteRecipe(String username, String slug, User deleter) {
|
|
||||||
this.recipeRepository.deleteRecipeByOwnerUsernameAndSlug(username, slug);
|
|
||||||
}
|
|
||||||
|
|
||||||
@PreAuthorize("@recipeSecurity.isViewableBy(#username, #slug, #viewer)")
|
@PreAuthorize("@recipeSecurity.isViewableBy(#username, #slug, #viewer)")
|
||||||
@Contract("_, _, null -> null")
|
@Contract("_, _, null -> null")
|
||||||
public @Nullable Boolean isStarer(String username, String slug, @Nullable User viewer) {
|
public @Nullable Boolean isStarer(String username, String slug, @Nullable User viewer) {
|
||||||
|
|||||||
@ -118,16 +118,6 @@ public class RecipesController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping("/{username}/{slug}")
|
|
||||||
public ResponseEntity<Void> deleteRecipe(
|
|
||||||
@AuthenticationPrincipal User principal,
|
|
||||||
@PathVariable String username,
|
|
||||||
@PathVariable String slug
|
|
||||||
) {
|
|
||||||
this.recipeService.deleteRecipe(username, slug, principal);
|
|
||||||
return ResponseEntity.noContent().build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@PostMapping("/{username}/{slug}/star")
|
@PostMapping("/{username}/{slug}/star")
|
||||||
public ResponseEntity<RecipeStar> addStar(
|
public ResponseEntity<RecipeStar> addStar(
|
||||||
@PathVariable String username,
|
@PathVariable String username,
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
package app.mealsmadeeasy.api.recipe.star;
|
package app.mealsmadeeasy.api.recipe.star;
|
||||||
|
|
||||||
import app.mealsmadeeasy.api.recipe.Recipe;
|
import jakarta.persistence.Column;
|
||||||
import app.mealsmadeeasy.api.user.User;
|
import jakarta.persistence.EmbeddedId;
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
import java.time.OffsetDateTime;
|
import java.time.OffsetDateTime;
|
||||||
@ -15,16 +16,6 @@ public final class RecipeStar {
|
|||||||
@EmbeddedId
|
@EmbeddedId
|
||||||
private RecipeStarId id;
|
private RecipeStarId id;
|
||||||
|
|
||||||
@ManyToOne
|
|
||||||
@MapsId("ownerId")
|
|
||||||
@JoinColumn(name = "owner_id", nullable = false, updatable = false)
|
|
||||||
private User owner;
|
|
||||||
|
|
||||||
@ManyToOne
|
|
||||||
@MapsId("recipeId")
|
|
||||||
@JoinColumn(name = "recipe_id", nullable = false, updatable = false)
|
|
||||||
private Recipe recipe;
|
|
||||||
|
|
||||||
@Column(nullable = false, updatable = false)
|
@Column(nullable = false, updatable = false)
|
||||||
private OffsetDateTime timestamp = OffsetDateTime.now();
|
private OffsetDateTime timestamp = OffsetDateTime.now();
|
||||||
|
|
||||||
|
|||||||
@ -1,47 +0,0 @@
|
|||||||
ALTER TABLE user_granted_authority DROP CONSTRAINT user_granted_authority_user_id_fkey;
|
|
||||||
ALTER TABLE user_granted_authority ADD FOREIGN KEY (user_id) REFERENCES "user" ON DELETE CASCADE;
|
|
||||||
|
|
||||||
ALTER TABLE refresh_token DROP CONSTRAINT refresh_token_owner_id_fkey;
|
|
||||||
ALTER TABLE refresh_token ADD FOREIGN KEY (owner_id) REFERENCES "user" ON DELETE CASCADE;
|
|
||||||
|
|
||||||
ALTER TABLE image DROP CONSTRAINT image_owner_id_fkey;
|
|
||||||
ALTER TABLE image ADD FOREIGN KEY (owner_id) REFERENCES "user" ON DELETE CASCADE;
|
|
||||||
|
|
||||||
ALTER TABLE image_viewer DROP CONSTRAINT image_viewer_image_id_fkey;
|
|
||||||
ALTER TABLE image_viewer ADD FOREIGN KEY (image_id) REFERENCES image ON DELETE CASCADE;
|
|
||||||
|
|
||||||
ALTER TABLE image_viewer DROP CONSTRAINT image_viewer_viewer_id_fkey;
|
|
||||||
ALTER TABLE image_viewer ADD FOREIGN KEY (viewer_id) REFERENCES "user" ON DELETE CASCADE;
|
|
||||||
|
|
||||||
ALTER TABLE recipe DROP CONSTRAINT recipe_owner_id_fkey;
|
|
||||||
ALTER TABLE recipe ADD FOREIGN KEY (owner_id) REFERENCES "user" ON DELETE CASCADE;
|
|
||||||
|
|
||||||
ALTER TABLE recipe DROP CONSTRAINT recipe_main_image_id_fkey;
|
|
||||||
ALTER TABLE recipe ADD FOREIGN KEY (main_image_id) REFERENCES image ON DELETE SET NULL;
|
|
||||||
|
|
||||||
ALTER TABLE recipe_viewer DROP CONSTRAINT recipe_viewer_recipe_id_fkey;
|
|
||||||
ALTER TABLE recipe_viewer ADD FOREIGN KEY (recipe_id) REFERENCES recipe ON DELETE CASCADE;
|
|
||||||
|
|
||||||
ALTER TABLE recipe_viewer DROP CONSTRAINT recipe_viewer_viewer_id_fkey;
|
|
||||||
ALTER TABLE recipe_viewer ADD FOREIGN KEY (viewer_id) REFERENCES "user" ON DELETE CASCADE;
|
|
||||||
|
|
||||||
ALTER TABLE recipe_comment DROP CONSTRAINT recipe_comment_owner_id_fkey;
|
|
||||||
ALTER TABLE recipe_comment ADD FOREIGN KEY (owner_id) REFERENCES "user" ON DELETE CASCADE;
|
|
||||||
|
|
||||||
ALTER TABLE recipe_comment DROP CONSTRAINT recipe_comment_recipe_id_fkey;
|
|
||||||
ALTER TABLE recipe_comment ADD FOREIGN KEY (recipe_id) REFERENCES recipe ON DELETE CASCADE;
|
|
||||||
|
|
||||||
ALTER TABLE recipe_star DROP CONSTRAINT recipe_star_recipe_id_fkey;
|
|
||||||
ALTER TABLE recipe_star ADD FOREIGN KEY (recipe_id) REFERENCES recipe ON DELETE CASCADE;
|
|
||||||
|
|
||||||
ALTER TABLE recipe_star DROP CONSTRAINT recipe_star_owner_id_fkey;
|
|
||||||
ALTER TABLE recipe_star ADD FOREIGN KEY (owner_id) REFERENCES "user" ON DELETE CASCADE;
|
|
||||||
|
|
||||||
ALTER TABLE recipe_draft DROP CONSTRAINT recipe_draft_owner_id_fkey;
|
|
||||||
ALTER TABLE recipe_draft ADD FOREIGN KEY (owner_id) REFERENCES "user" ON DELETE CASCADE;
|
|
||||||
|
|
||||||
ALTER TABLE recipe_draft DROP CONSTRAINT recipe_draft_main_image_id_fkey;
|
|
||||||
ALTER TABLE recipe_draft ADD FOREIGN KEY (main_image_id) REFERENCES image ON DELETE SET NULL;
|
|
||||||
|
|
||||||
ALTER TABLE file DROP CONSTRAINT file_owner_id_fkey;
|
|
||||||
ALTER TABLE file ADD FOREIGN KEY (owner_id) REFERENCES "user" ON DELETE CASCADE;
|
|
||||||
Loading…
Reference in New Issue
Block a user