From 07bde2afd4958fcf79e5bc90303784867319a23b Mon Sep 17 00:00:00 2001 From: Madeleine Stamp Date: Wed, 15 Apr 2026 19:36:35 -0600 Subject: [PATCH] Update 12 files - /demo/src/main/java/com/example/demo/entity/User.java - /demo/src/main/java/com/example/demo/entity/Recipe.java - /demo/src/main/java/com/example/demo/service/Impl/RecipeServiceImpl.java - /demo/src/main/java/com/example/demo/service/Impl/UserServiceImpl.java - /demo/src/main/java/com/example/demo/service/CustomUserDetailsService.java - /demo/src/main/java/com/example/demo/service/UserService.java - /demo/src/main/java/com/example/demo/service/RecipeService.java - /demo/src/main/java/com/example/demo/repository/RecipeRepo.java - /demo/src/main/java/com/example/demo/config/SecurityConfig.java - /demo/src/main/java/com/example/demo/controller/RecipeController.java - /demo/src/main/java/com/example/demo/controller/UserController.java - /demo/src/main/java/com/example/demo/controller/AdminController.java --- .../example/demo/config/SecurityConfig.java | 43 +++-- .../demo/controller/AdminController.java | 39 ++++ .../demo/controller/RecipeController.java | 23 ++- .../demo/controller/UserController.java | 24 +-- .../java/com/example/demo/entity/Recipe.java | 15 +- .../java/com/example/demo/entity/User.java | 79 +++++--- .../example/demo/repository/RecipeRepo.java | 5 + .../service/CustomUserDetailsService.java | 7 +- .../demo/service/Impl/RecipeServiceImpl.java | 173 +++++++++++------- .../demo/service/Impl/UserServiceImpl.java | 41 ++++- .../example/demo/service/RecipeService.java | 7 +- .../com/example/demo/service/UserService.java | 14 +- 12 files changed, 315 insertions(+), 155 deletions(-) create mode 100644 demo/src/main/java/com/example/demo/controller/AdminController.java diff --git a/demo/src/main/java/com/example/demo/config/SecurityConfig.java b/demo/src/main/java/com/example/demo/config/SecurityConfig.java index 0507a6a..8cadc1d 100644 --- a/demo/src/main/java/com/example/demo/config/SecurityConfig.java +++ b/demo/src/main/java/com/example/demo/config/SecurityConfig.java @@ -9,24 +9,29 @@ import org.springframework.security.web.SecurityFilterChain; @Configuration public class SecurityConfig { - @Bean - public PasswordEncoder passwordEncoder() { - return new BCryptPasswordEncoder(); - } - @Bean - public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { - http - .authorizeHttpRequests(auth -> auth - .requestMatchers("/login", "/register", "/css/**", "/images/**").permitAll() - .anyRequest().authenticated() - ) - .formLogin(form -> form - .loginPage("/login") - .defaultSuccessUrl("/", true) - .permitAll() - ) - .logout(logout -> logout.permitAll()); - return http.build(); - } + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http + .csrf(csrf -> csrf.disable()) + .authorizeHttpRequests(auth -> auth + .requestMatchers("/login", "/register", "/css/**", "/images/**").permitAll() + .requestMatchers("/api/users").permitAll() + .requestMatchers("/api/admin/**").hasRole("ADMIN") + .anyRequest().authenticated() + ) + .formLogin(form -> form + .loginPage("/login") + .defaultSuccessUrl("/", true) + .permitAll() + ) + .logout(logout -> logout.permitAll()); + + return http.build(); + } } \ No newline at end of file diff --git a/demo/src/main/java/com/example/demo/controller/AdminController.java b/demo/src/main/java/com/example/demo/controller/AdminController.java new file mode 100644 index 0000000..e3fabcf --- /dev/null +++ b/demo/src/main/java/com/example/demo/controller/AdminController.java @@ -0,0 +1,39 @@ +package com.example.demo.controller; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import com.example.demo.dto.UserDto; +import com.example.demo.service.UserService; + +@RestController +@RequestMapping("/api/admin") +public class AdminController { + + private final UserService userService; + + public AdminController(UserService userService) { + this.userService = userService; + } + + @PostMapping("/users/{id}/ban") + public ResponseEntity banUser(@PathVariable Integer id) { + return new ResponseEntity<>(userService.banUser(id), HttpStatus.OK); + } + + @PostMapping("/users/{id}/unban") + public ResponseEntity unbanUser(@PathVariable Integer id) { + return new ResponseEntity<>(userService.unbanUser(id), HttpStatus.OK); + } + + @PostMapping("/users/{id}/make-admin") + public ResponseEntity makeAdmin(@PathVariable Integer id) { + return new ResponseEntity<>(userService.makeAdmin(id), HttpStatus.OK); + } + + @PostMapping("/users/{id}/make-user") + public ResponseEntity makeUser(@PathVariable Integer id) { + return new ResponseEntity<>(userService.makeUser(id), HttpStatus.OK); + } +} diff --git a/demo/src/main/java/com/example/demo/controller/RecipeController.java b/demo/src/main/java/com/example/demo/controller/RecipeController.java index 769f988..360314c 100644 --- a/demo/src/main/java/com/example/demo/controller/RecipeController.java +++ b/demo/src/main/java/com/example/demo/controller/RecipeController.java @@ -13,6 +13,7 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import org.springframework.security.core.Authentication; import com.example.demo.dto.RecipeDto; import com.example.demo.dto.UserDto; @@ -35,9 +36,9 @@ public class RecipeController { // build create recipe REST API @PostMapping - public ResponseEntity saveRecipe(@Valid @RequestBody Recipe recipe) { - RecipeDto recipeDto = recipeService.convertToDto(recipe); - return new ResponseEntity(recipeService.saveRecipe(recipeDto), HttpStatus.CREATED); + public ResponseEntity saveRecipe(@RequestBody RecipeDto recipeDto, Authentication authentication) { + String currentUsername = authentication.getName(); + return new ResponseEntity<>(recipeService.saveRecipe(recipeDto, currentUsername), HttpStatus.CREATED); } // build get all recipes REST API @@ -69,17 +70,21 @@ public class RecipeController { // build update recipe REST API // http://localhost:8080/api/recipes/(id number goes here) @PutMapping("{id}") - public ResponseEntity updateRecipe(@PathVariable("id") Integer recipeId, @RequestBody Recipe recipe) { - RecipeDto recipeDto = recipeService.convertToDto(recipe); - return new ResponseEntity(recipeService.updateRecipe(recipeDto, recipeId), HttpStatus.OK); + public ResponseEntity updateRecipe( + @PathVariable("id") Integer recipeId, + @RequestBody RecipeDto recipeDto, + Authentication authentication) { + String currentUsername = authentication.getName(); + return new ResponseEntity<>(recipeService.updateRecipe(recipeDto, recipeId, currentUsername), HttpStatus.OK); } // build delete recipe REST API // http://localhost:8080/api/recipes/(id number goes here) @DeleteMapping("{id}") - public ResponseEntity deleteRecipe(@PathVariable("id") Integer recipeId) { - recipeService.deleteRecipe(recipeId); - return new ResponseEntity("Recipe deleted succesfully!", HttpStatus.OK); + public ResponseEntity deleteRecipe(@PathVariable("id") Integer recipeId, Authentication authentication) { + String currentUsername = authentication.getName(); + recipeService.deleteRecipe(recipeId, currentUsername); + return new ResponseEntity<>("Recipe deleted successfully!", HttpStatus.OK); } } diff --git a/demo/src/main/java/com/example/demo/controller/UserController.java b/demo/src/main/java/com/example/demo/controller/UserController.java index 4e59155..9a8031f 100644 --- a/demo/src/main/java/com/example/demo/controller/UserController.java +++ b/demo/src/main/java/com/example/demo/controller/UserController.java @@ -1,8 +1,6 @@ package com.example.demo.controller; -import java.security.Principal; import java.util.List; -import java.util.Optional; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -15,26 +13,20 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.core.userdetails.UserDetails; import com.example.demo.dto.UserDto; import com.example.demo.entity.User; import com.example.demo.service.UserService; -import com.example.demo.repository.UserRepo; @RestController @RequestMapping("/api/users") public class UserController { private UserService userService; - private UserRepo userRepo; - public UserController(UserService userService, UserRepo userRepo) { + public UserController(UserService userService) { super(); this.userService = userService; - this.userRepo = userRepo; } // build create user REST API @@ -52,20 +44,10 @@ public class UserController { // build get user by name REST API @GetMapping("/search") - public ResponseEntity> getUsersByName(@RequestParam String string) { - List users = userService.getUsersByName(string); + public ResponseEntity> getUsersByName(@RequestParam String name) { + List users = userService.getUsersByName(name); return new ResponseEntity<>(users, HttpStatus.OK); } - - // build get current user REST API - @GetMapping("/me") - public UserDto getLoggedInUser(Principal principal) { - if (principal == null) return null; - String username = principal.getName(); - User user = (userRepo.findByUsername(username)) - .orElse(null); - return userService.convertToDto(user); - } // build get user by id REST API diff --git a/demo/src/main/java/com/example/demo/entity/Recipe.java b/demo/src/main/java/com/example/demo/entity/Recipe.java index 97bbb49..616ed38 100644 --- a/demo/src/main/java/com/example/demo/entity/Recipe.java +++ b/demo/src/main/java/com/example/demo/entity/Recipe.java @@ -5,7 +5,6 @@ import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Positive; -import jakarta.validation.constraints.Size; import lombok.EqualsAndHashCode; import java.math.BigDecimal; @@ -20,21 +19,18 @@ public class Recipe { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; - - @Column(columnDefinition = "TEXT") + @NotBlank(message = "Please provide a recipe title") - @Size(max = 128, message = "Title cannot be longer than 128 characters") private String title; @Column(columnDefinition = "TEXT") - @Size(max = 500, message = "Description cannot be longer than 500 characters") private String description; - @NotNull(message = "Please Provide a prep time amount in minutes") + @NotNull(message = "Please Provide a prep time amount in mintutes") @Positive(message = "This value cannot be negative") private Integer prepTimeMinutes; - @NotNull(message = "Please Provide a cook time amount in minutes") + @NotNull(message = "Please Provide a cook time amount in mintutes") @Positive(message = "This value cannot be negative") private Integer cookTimeMinutes; @@ -55,14 +51,11 @@ public class Recipe { // Recipe ingredients relationship @OneToMany(mappedBy = "recipe", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY) - @NotEmpty(message = "At least one ingredient is required") - @Size(max = 256, message = "Cannot have more than 256 ingredients") + @NotEmpty(message = "At least one ingredient is required") private Set recipeIngredients = new HashSet<>(); // Recipe Steps relationship @OneToMany(mappedBy = "recipe", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY) - @NotEmpty(message = "At least one step is required") - @Size(max = 50, message = "Cannot have more than 50 steps") private Set steps = new HashSet<>(); // Recipe Images relationship diff --git a/demo/src/main/java/com/example/demo/entity/User.java b/demo/src/main/java/com/example/demo/entity/User.java index a48e027..f7b0a1d 100644 --- a/demo/src/main/java/com/example/demo/entity/User.java +++ b/demo/src/main/java/com/example/demo/entity/User.java @@ -1,28 +1,26 @@ package com.example.demo.entity; +import jakarta.persistence.CascadeType; +import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.FetchType; -import jakarta.persistence.Table; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; import jakarta.persistence.Id; import jakarta.persistence.JoinColumn; import jakarta.persistence.JoinTable; import jakarta.persistence.ManyToMany; import jakarta.persistence.OneToMany; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.CascadeType; -import jakarta.persistence.Column; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.userdetails.UserDetails; -import com.fasterxml.jackson.annotation.JsonIgnore; +import jakarta.persistence.Table; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; import java.time.LocalDateTime; -import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; +import java.util.List; import java.util.Set; @Entity @@ -31,17 +29,15 @@ public class User implements UserDetails { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) - @NotNull private Integer id; @Column(nullable = false, unique = true) - @NotBlank(message = "Username is required") private String username; + @Column(nullable = false) private String role; @Column(unique = true) - @NotBlank(message = "Email is required") private String email; private String hashedpassword; @@ -49,20 +45,23 @@ public class User implements UserDetails { @Column(name = "created_at") private LocalDateTime createdAt; - // User Recipe relationship + @Column(nullable = false) + private boolean banned = false; + @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY) private Set recipes = new HashSet<>(); - // Favorite relationship and also junction table @ManyToMany(fetch = FetchType.LAZY) - @JsonIgnore - @JoinTable(name = "favorites", joinColumns = { @JoinColumn(name = "userId") }, inverseJoinColumns = { - @JoinColumn(name = "recipeId") }) + @JoinTable( + name = "favorites", + joinColumns = { @JoinColumn(name = "userId") }, + inverseJoinColumns = { @JoinColumn(name = "recipeId") } + ) private Set FavRecipes = new HashSet<>(); @Override public Collection getAuthorities() { - return new ArrayList<>(); + return List.of(new SimpleGrantedAuthority(role)); } @Override @@ -75,6 +74,26 @@ public class User implements UserDetails { return username; } + @Override + public boolean isAccountNonExpired() { + return true; + } + + @Override + public boolean isAccountNonLocked() { + return !banned; + } + + @Override + public boolean isCredentialsNonExpired() { + return true; + } + + @Override + public boolean isEnabled() { + return !banned; + } + public User() { } @@ -84,6 +103,7 @@ public class User implements UserDetails { this.email = email; this.hashedpassword = hashedpassword; this.createdAt = createdAt; + this.banned = false; } public Integer getId() { @@ -130,12 +150,27 @@ public class User implements UserDetails { this.createdAt = createdAt; } + public boolean isBanned() { + return banned; + } + + public void setBanned(boolean banned) { + this.banned = banned; + } + + public Set getRecipes() { + return recipes; + } + + public void setRecipes(Set recipes) { + this.recipes = recipes; + } + public Set getFavRecipes() { return FavRecipes; } public void setFavRecipes(Set favRecipes) { - FavRecipes = favRecipes; + this.FavRecipes = favRecipes; } - } \ No newline at end of file diff --git a/demo/src/main/java/com/example/demo/repository/RecipeRepo.java b/demo/src/main/java/com/example/demo/repository/RecipeRepo.java index 36d44c1..979fe65 100644 --- a/demo/src/main/java/com/example/demo/repository/RecipeRepo.java +++ b/demo/src/main/java/com/example/demo/repository/RecipeRepo.java @@ -1,5 +1,6 @@ package com.example.demo.repository; +import java.time.LocalDateTime; import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; @@ -9,5 +10,9 @@ public interface RecipeRepo extends JpaRepository { List findByTitleContainingIgnoreCase(String name); List findByTitleContainingIgnoreCaseAndTags_NameIn(String title, List tags); + + long countByUserIdAndCreatedAtAfter(Integer userId, LocalDateTime after); + + List findByUserId(Integer userId); } \ No newline at end of file diff --git a/demo/src/main/java/com/example/demo/service/CustomUserDetailsService.java b/demo/src/main/java/com/example/demo/service/CustomUserDetailsService.java index e527f07..6ad9739 100644 --- a/demo/src/main/java/com/example/demo/service/CustomUserDetailsService.java +++ b/demo/src/main/java/com/example/demo/service/CustomUserDetailsService.java @@ -17,7 +17,8 @@ public class CustomUserDetailsService implements UserDetailsService { } @Override - public UserDetails loadUserByUsername(@NonNull String username) throws UsernameNotFoundException { - return userRepo.findByUsername(username).orElseThrow(() -> new UsernameNotFoundException("User not found")); - } + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + return userRepo.findByUsername(username) + .orElseThrow(() -> new UsernameNotFoundException("User not found: " + username)); + } } diff --git a/demo/src/main/java/com/example/demo/service/Impl/RecipeServiceImpl.java b/demo/src/main/java/com/example/demo/service/Impl/RecipeServiceImpl.java index b665432..da898cb 100644 --- a/demo/src/main/java/com/example/demo/service/Impl/RecipeServiceImpl.java +++ b/demo/src/main/java/com/example/demo/service/Impl/RecipeServiceImpl.java @@ -1,9 +1,11 @@ package com.example.demo.service.Impl; +import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; +import org.springframework.security.access.AccessDeniedException; import org.springframework.stereotype.Service; import com.example.demo.dto.RecipeDto; @@ -55,6 +57,7 @@ public class RecipeServiceImpl implements RecipeService { this.tagRepository = tagRepository; } + @Override public RecipeDto convertToDto(Recipe recipe) { List ingredientDtos = recipe.getRecipeIngredients().stream() .map(ri -> new RecipeIngredientDto(ri.getIngredient().getName(), ri.getQuantity(), ri.getUnit(), @@ -71,34 +74,79 @@ public class RecipeServiceImpl implements RecipeService { UserDto userDto = new UserDto(recipe.getUser().getId(), recipe.getUser().getUsername(), recipe.getUser().getEmail()); - return new RecipeDto(recipe.getTitle(), recipe.getDescription(), recipe.getPrepTimeMinutes(), + RecipeDto dto = new RecipeDto(recipe.getTitle(), recipe.getDescription(), recipe.getPrepTimeMinutes(), recipe.getCookTimeMinutes(), recipe.getServings(), userDto, recipe.getStatus(), ingredientDtos, stepDtos, imageDtos, tagDtos); + + dto.setId(recipe.getId()); + + return dto; + } + + private User getCurrentUser(String currentUsername) { + return userRepository.findByUsername(currentUsername) + .orElseThrow(() -> new NotFoundException("User", "username", currentUsername)); + } + + private boolean isAdmin(User user) { + return "ROLE_ADMIN".equals(user.getRole()); + } + + private void ensureUserNotBanned(User user) { + if (user.isBanned()) { + throw new AccessDeniedException("Banned users cannot perform this action."); + } + } + + private void enforceUploadLimit(User user) { + if (isAdmin(user)) { + return; + } + + LocalDateTime cutoff = LocalDateTime.now().minusHours(24); + long uploadsInLast24Hours = recipeRepository.countByUserIdAndCreatedAtAfter(user.getId(), cutoff); + + if (uploadsInLast24Hours >= 10) { + throw new AccessDeniedException("Upload limit reached. Maximum is 10 recipes per 24 hours."); + } + } + + private void enforceOwnerOrAdmin(User currentUser, Recipe recipe) { + if (isAdmin(currentUser)) { + return; + } + + if (!recipe.getUser().getId().equals(currentUser.getId())) { + throw new AccessDeniedException("You do not have permission to modify this recipe."); + } } @Override @Transactional - public RecipeDto saveRecipe(RecipeDto dto) { + public RecipeDto saveRecipe(RecipeDto dto, String currentUsername) { - User user = userRepository.findById(dto.getUserDto().getId()) - .orElseThrow(() -> new NotFoundException("User", "id", dto.getUserDto().getId())); + User currentUser = getCurrentUser(currentUsername); + ensureUserNotBanned(currentUser); + enforceUploadLimit(currentUser); Recipe recipe = new Recipe(dto.getTitle(), dto.getDescription(), dto.getPrepTimeMinutes(), - dto.getCookTimeMinutes(), dto.getServings(), user, dto.getStatus()); + dto.getCookTimeMinutes(), dto.getServings(), currentUser, dto.getStatus()); - for (RecipeIngredientDto riDto : dto.getIngredients()) { + if (dto.getIngredients() != null) { + for (RecipeIngredientDto riDto : dto.getIngredients()) { - Ingredient ingredient = ingredientRepository.findByNameIgnoreCase(riDto.getIngredientName()) - .orElseGet(() -> new Ingredient(riDto.getIngredientName())); + Ingredient ingredient = ingredientRepository.findByNameIgnoreCase(riDto.getIngredientName()) + .orElseGet(() -> new Ingredient(riDto.getIngredientName())); - if (ingredient.getId() == null) { - ingredientRepository.save(ingredient); + if (ingredient.getId() == null) { + ingredientRepository.save(ingredient); + } + + RecipeIngredient ri = new RecipeIngredient(recipe, ingredient, riDto.getQuantity(), riDto.getUnit(), + riDto.getNotes()); + + recipe.getRecipeIngredients().add(ri); } - - RecipeIngredient ri = new RecipeIngredient(recipe, ingredient, riDto.getQuantity(), riDto.getUnit(), - riDto.getNotes()); - - recipe.getRecipeIngredients().add(ri); } if (dto.getSteps() != null) { @@ -115,20 +163,21 @@ public class RecipeServiceImpl implements RecipeService { } } - for (TagDto tDto : dto.getTags()) { + if (dto.getTags() != null) { + for (TagDto tDto : dto.getTags()) { - Tag tag = tagRepository.findByName(tDto.getName()).orElseGet(() -> new Tag(tDto.getName())); + Tag tag = tagRepository.findByName(tDto.getName()).orElseGet(() -> new Tag(tDto.getName())); - if (tag.getId() == null) { - tagRepository.save(tag); + if (tag.getId() == null) { + tagRepository.save(tag); + } + recipe.getTags().add(tag); } - recipe.getTags().add(tag); } Recipe saved = recipeRepository.save(recipe); return getRecipeById(saved.getId()); - //return convertToDto(saved); } @Override @@ -152,10 +201,15 @@ public class RecipeServiceImpl implements RecipeService { @Override @Transactional - public RecipeDto updateRecipe(RecipeDto recipeDto, Integer id) { + public RecipeDto updateRecipe(RecipeDto recipeDto, Integer id, String currentUsername) { + User currentUser = getCurrentUser(currentUsername); + ensureUserNotBanned(currentUser); + Recipe existingRecipe = recipeRepository.findById(id) .orElseThrow(() -> new NotFoundException("Recipe", "id", id)); + enforceOwnerOrAdmin(currentUser, existingRecipe); + existingRecipe.setTitle(recipeDto.getTitle()); existingRecipe.setDescription(recipeDto.getDescription()); existingRecipe.setPrepTimeMinutes(recipeDto.getPrepTimeMinutes()); @@ -197,13 +251,10 @@ public class RecipeServiceImpl implements RecipeService { for (RecipeIngredientDto riDto : updatedIngredients) { - // go through the old list of ingredients until we find a match with updated - // list RecipeIngredient existingRI = existingRecipe.getRecipeIngredients().stream() .filter(ri -> ri.getIngredient().getName().equals(riDto.getIngredientName())).findFirst() .orElse(null); - // if old ingredient just update parameters if (existingRI != null) { existingRI.setQuantity(riDto.getQuantity()); @@ -211,7 +262,6 @@ public class RecipeServiceImpl implements RecipeService { existingRI.setNotes(riDto.getNotes()); } - // if new ingredient, have to make a whole new thing else { Ingredient ingredient = ingredientRepository.findByNameIgnoreCase(riDto.getIngredientName()) @@ -229,7 +279,6 @@ public class RecipeServiceImpl implements RecipeService { } if (updatedSteps != null) { - // find steps that weren't included for (Step step : existingRecipe.getSteps()) { boolean existsInUpdatedList = updatedSteps.stream() .anyMatch(dto -> dto.getStepNumber().equals(step.getStepNumber())); @@ -237,22 +286,17 @@ public class RecipeServiceImpl implements RecipeService { if (!existsInUpdatedList) stepsToRemove.add(step); } - // delete those steps existingRecipe.getSteps().removeAll(stepsToRemove); - // go through updated steps for (StepDto stepDto : updatedSteps) { - // find matching step by step number Step existingStep = existingRecipe.getSteps().stream() .filter(s -> s.getStepNumber().equals(stepDto.getStepNumber())).findFirst().orElse(null); - // if there's a match update the instruction string if (existingStep != null) { existingStep.setInstruction(stepDto.getInstruction()); } - // if no match then make a whole new step else { Step newStep = new Step(existingRecipe, stepDto.getStepNumber(), stepDto.getInstruction()); existingRecipe.getSteps().add(newStep); @@ -260,7 +304,6 @@ public class RecipeServiceImpl implements RecipeService { } } - // same process as above just with images instead if (updatedImages != null) { for (Image image : existingRecipe.getImages()) { boolean existsInUpdatedList = updatedImages.stream() @@ -284,8 +327,6 @@ public class RecipeServiceImpl implements RecipeService { } } - // same process as above just with tags instead, except for saving the tag - // since the relationship for this one was slightly different if (updatedTags != null) { for (Tag tag : existingRecipe.getTags()) { boolean existsInUpdatedList = updatedTags.stream().anyMatch(dto -> dto.getName().equals(tag.getName())); @@ -314,39 +355,45 @@ public class RecipeServiceImpl implements RecipeService { } @Override - public void deleteRecipe(Integer Id) { - recipeRepository.findById(Id).orElseThrow(() -> new NotFoundException("Recipe", "id", Id)); - recipeRepository.deleteById(Id); + @Transactional + public void deleteRecipe(Integer id, String currentUsername) { + User currentUser = getCurrentUser(currentUsername); + ensureUserNotBanned(currentUser); + Recipe recipe = recipeRepository.findById(id) + .orElseThrow(() -> new NotFoundException("Recipe", "id", id)); + + enforceOwnerOrAdmin(currentUser, recipe); + recipeRepository.delete(recipe); } - @Override + @Override @Transactional public List getRecipes(String name, List tags) { - - List recipes; - - if(!name.isBlank()) { - recipes = recipeRepository.findByTitleContainingIgnoreCase(name); - } - - else { - recipes = recipeRepository.findAll(); - } - - if(!tags.isEmpty() && !recipes.isEmpty()) { - recipes = recipes.stream() - .filter(recipe -> recipe.getTags().stream().anyMatch(tag -> tags.contains(tag.getName()))) - .collect(Collectors.toList()); - } - - List recipeList = new ArrayList<>(); - for (Recipe recipe : recipes) { - RecipeDto dto = convertToDto(recipe); - recipeList.add(dto); - } - - return recipeList; + List recipes; + + if (!name.isBlank()) { + recipes = recipeRepository.findByTitleContainingIgnoreCase(name); + } + + else { + recipes = recipeRepository.findAll(); + } + + if (!tags.isEmpty() && !recipes.isEmpty()) { + recipes = recipes.stream() + .filter(recipe -> recipe.getTags().stream().anyMatch(tag -> tags.contains(tag.getName()))) + .collect(Collectors.toList()); + } + + List recipeList = new ArrayList<>(); + + for (Recipe recipe : recipes) { + RecipeDto dto = convertToDto(recipe); + recipeList.add(dto); + } + + return recipeList; } } \ No newline at end of file diff --git a/demo/src/main/java/com/example/demo/service/Impl/UserServiceImpl.java b/demo/src/main/java/com/example/demo/service/Impl/UserServiceImpl.java index 2a2779f..dc2cb36 100644 --- a/demo/src/main/java/com/example/demo/service/Impl/UserServiceImpl.java +++ b/demo/src/main/java/com/example/demo/service/Impl/UserServiceImpl.java @@ -1,4 +1,4 @@ -package com.example.demo.service.Impl; +ppackage com.example.demo.service.Impl; import java.util.ArrayList; import java.util.List; @@ -39,6 +39,10 @@ public class UserServiceImpl implements UserService { @Override public User saveUser(User user) { + if (user.getRole() == null || user.getRole().isBlank()) { + user.setRole("ROLE_USER"); + } + user.setBanned(false); return userRepository.save(user); } @@ -125,4 +129,39 @@ public class UserServiceImpl implements UserService { return userList; } + @Override + public UserDto banUser(Integer id) { + User user = userRepository.findById(id) + .orElseThrow(() -> new NotFoundException("User", "id", id)); + user.setBanned(true); + userRepository.save(user); + return convertToDto(user); + } + + @Override + public UserDto unbanUser(Integer id) { + User user = userRepository.findById(id) + .orElseThrow(() -> new NotFoundException("User", "id", id)); + user.setBanned(false); + userRepository.save(user); + return convertToDto(user); + } + + @Override + public UserDto makeAdmin(Integer id) { + User user = userRepository.findById(id) + .orElseThrow(() -> new NotFoundException("User", "id", id)); + user.setRole("ROLE_ADMIN"); + userRepository.save(user); + return convertToDto(user); + } + + @Override + public UserDto makeUser(Integer id) { + User user = userRepository.findById(id) + .orElseThrow(() -> new NotFoundException("User", "id", id)); + user.setRole("ROLE_USER"); + userRepository.save(user); + return convertToDto(user); + } } diff --git a/demo/src/main/java/com/example/demo/service/RecipeService.java b/demo/src/main/java/com/example/demo/service/RecipeService.java index 1efcc5a..623a07c 100644 --- a/demo/src/main/java/com/example/demo/service/RecipeService.java +++ b/demo/src/main/java/com/example/demo/service/RecipeService.java @@ -11,7 +11,7 @@ import com.example.demo.entity.User; public interface RecipeService { RecipeDto convertToDto(Recipe recipe); - RecipeDto saveRecipe(RecipeDto recipe); + RecipeDto saveRecipe(RecipeDto recipe, String currentUsername); List getAllRecipes(); @@ -19,8 +19,9 @@ public interface RecipeService { List getRecipes(String name, List tags); - RecipeDto updateRecipe(RecipeDto recipedto, Integer Id); + RecipeDto updateRecipe(RecipeDto recipedto, Integer Id, String currentUsername); - void deleteRecipe(Integer Id); + void deleteRecipe(Integer Id, String currentUsername); } + diff --git a/demo/src/main/java/com/example/demo/service/UserService.java b/demo/src/main/java/com/example/demo/service/UserService.java index 6038023..cf315f5 100644 --- a/demo/src/main/java/com/example/demo/service/UserService.java +++ b/demo/src/main/java/com/example/demo/service/UserService.java @@ -12,15 +12,23 @@ public interface UserService { List getAllUsers(); - UserDto getUserById(Integer Id); + UserDto getUserById(Integer id); List getUsersByName(String name); UserDto saveFavorite(Integer userId, Integer recipeId); - UserDto updateUser(User user, Integer Id); + UserDto updateUser(User user, Integer id); - void deleteUser(Integer Id); + void deleteUser(Integer id); void deleteFavorite(Integer userId, Integer recipeId); + + UserDto banUser(Integer id); + + UserDto unbanUser(Integer id); + + UserDto makeAdmin(Integer id); + + UserDto makeUser(Integer id); }