security hardening #12

Merged
Cedric merged 15 commits from feature/security-hardening into main 2026-05-05 17:13:54 +02:00
3 changed files with 18 additions and 38 deletions
Showing only changes of commit 906b60d264 - Show all commits
@@ -16,11 +16,9 @@ public interface ExpenseListRepository extends JpaRepository<ExpenseList, Long>
ExpenseList findByInviteCode(String inviteCode);
@Query("SELECT el FROM ExpenseList el WHERE el.owner.id = :userId OR el.sharedWith.id = :sharedUserId")
List<ExpenseList> findByOwnerIdOrSharedWithId(@Param("userId") Long userId,
@Param("sharedUserId") Long sharedUserId);
@Query("SELECT el FROM ExpenseList el WHERE el.owner.id = :userId OR el.sharedWith.id = :userId")
List<ExpenseList> findByOwnerIdOrSharedWithId(@Param("userId") Long userId);
@Query("SELECT el FROM ExpenseList el WHERE el.owner.username = :username OR el.sharedWith.username = :sharedUsername")
List<ExpenseList> findByOwnerUsernameOrSharedWithUsername(@Param("username") String username,
@Param("sharedUsername") String sharedUsername);
@Query("SELECT el FROM ExpenseList el WHERE el.owner.username = :username OR el.sharedWith.username = :username")
List<ExpenseList> findByOwnerUsernameOrSharedWithUsername(@Param("username") String username);
}
@@ -1,8 +1,6 @@
package de.zendric.app.xpensely_server.services;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
@@ -10,9 +8,9 @@ import java.util.UUID;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import de.zendric.app.xpensely_server.model.AppUser;
import de.zendric.app.xpensely_server.model.Expense;
import de.zendric.app.xpensely_server.model.ExpenseList;
import de.zendric.app.xpensely_server.model.Exception.ResourceNotFoundException;
import de.zendric.app.xpensely_server.model.XpenselyCustomCategory;
import de.zendric.app.xpensely_server.repo.ExpenseListRepository;
import de.zendric.app.xpensely_server.repo.ExpenseRepository;
@@ -62,40 +60,24 @@ public class ExpenseListService {
}
public List<ExpenseList> findByUserId(Long id) {
return repository.findByOwnerIdOrSharedWithId(id, id);
return repository.findByOwnerIdOrSharedWithId(id);
}
public List<ExpenseList> findByUsername(String username) {
return repository.findByOwnerUsernameOrSharedWithUsername(username, username);
return repository.findByOwnerUsernameOrSharedWithUsername(username);
}
public Expense addExpenseToList(Long expenseListId, Expense expense) {
// find expenseList
ExpenseList expenseList = repository.findById(expenseListId)
.orElseThrow(() -> new RuntimeException("ExpenseList not found with id: " + expenseListId));
// get all added expenses
HashSet<Long> existingId = new HashSet<>();
for (Expense e : expenseList.getExpenses()) {
existingId.add(e.getId());
}
// add the new expense
.orElseThrow(() -> new ResourceNotFoundException("ExpenseList not found with id: " + expenseListId));
expenseList.addExpense(expense);
// save
repository.save(expenseList);
Expense newExpense = new Expense();
for (Expense e : expenseList.getExpenses()) {
if (!existingId.contains(e.getId())) {
newExpense = e;
break;
}
}
return newExpense;
return expense;
}
public void deleteExpenseFromList(Long expenseListId, Long expenseId) {
ExpenseList expenseList = repository.findById(expenseListId)
.orElseThrow(() -> new RuntimeException("ExpenseList not found with id: " + expenseListId));
.orElseThrow(() -> new ResourceNotFoundException("ExpenseList not found with id: " + expenseListId));
Expense expenseToRemove = null;
for (Expense expense : expenseList.getExpenses()) {
if (expense.getId().equals(expenseId)) {
@@ -106,14 +88,14 @@ public class ExpenseListService {
if (expenseToRemove != null) {
expenseList.removeExpense(expenseToRemove);
} else {
throw new RuntimeException("Expense not found with id: " + expenseId);
throw new ResourceNotFoundException("Expense not found with id: " + expenseId);
}
repository.save(expenseList);
}
public String generateInviteCode(Long listId) {
ExpenseList list = repository.findById(listId)
.orElseThrow(() -> new RuntimeException("List not found"));
.orElseThrow(() -> new ResourceNotFoundException("List not found"));
String inviteCode;
if (list.getInviteCode() == null || list.getInviteCodeExpiration().isBefore(LocalDateTime.now())) {
@@ -158,7 +140,7 @@ public class ExpenseListService {
// TODO implement API for this
public XpenselyCustomCategory addCustomCategory(Long expenseListId, XpenselyCustomCategory customCategory) {
ExpenseList expenseList = repository.findById(expenseListId)
.orElseThrow(() -> new RuntimeException("Expense List not found"));
.orElseThrow(() -> new ResourceNotFoundException("Expense List not found"));
customCategory.setExpenseList(expenseList);
return customCategoryRepository.save(customCategory);
@@ -167,7 +149,7 @@ public class ExpenseListService {
// TODO implement API for this
public void deleteCustomCategory(Long expenseListId, Long categoryId) {
XpenselyCustomCategory category = customCategoryRepository.findById(categoryId)
.orElseThrow(() -> new RuntimeException("Custom Category not found"));
.orElseThrow(() -> new ResourceNotFoundException("Custom Category not found"));
if (!category.getExpenseList().getId().equals(expenseListId)) {
throw new RuntimeException("Category does not belong to the specified Expense List");
}
@@ -33,12 +33,12 @@ class ExpenseListServiceTest {
void findByUserId_usesRepositoryQuery_notFindAll() {
AppUser owner = new AppUser(); owner.setId(1L);
ExpenseList list = new ExpenseList(); list.setId(10L); list.setOwner(owner);
when(repository.findByOwnerIdOrSharedWithId(1L, 1L)).thenReturn(List.of(list));
when(repository.findByOwnerIdOrSharedWithId(1L)).thenReturn(List.of(list));
List<ExpenseList> result = service.findByUserId(1L);
assertThat(result).hasSize(1);
verify(repository).findByOwnerIdOrSharedWithId(1L, 1L);
verify(repository).findByOwnerIdOrSharedWithId(1L);
verify(repository, never()).findAll();
}
@@ -46,12 +46,12 @@ class ExpenseListServiceTest {
void findByUsername_usesRepositoryQuery_notFindAll() {
AppUser owner = new AppUser(); owner.setId(1L); owner.setUsername("alice");
ExpenseList list = new ExpenseList(); list.setId(10L); list.setOwner(owner);
when(repository.findByOwnerUsernameOrSharedWithUsername("alice", "alice")).thenReturn(List.of(list));
when(repository.findByOwnerUsernameOrSharedWithUsername("alice")).thenReturn(List.of(list));
List<ExpenseList> result = service.findByUsername("alice");
assertThat(result).hasSize(1);
verify(repository).findByOwnerUsernameOrSharedWithUsername("alice", "alice");
verify(repository).findByOwnerUsernameOrSharedWithUsername("alice");
verify(repository, never()).findAll();
}
}