Jak testować metodę serwisową z adnotacją @PreAuthorize?

0

Cześć. Chciałem uzyskać trochę informacji odnośnie Spring Security oraz testów.

  1. Gdzie powinna znajdować się adnotacja @PreAuthorize ? Nad metodą serwisu czy nad metodą kontrolera, która wystawia endpoint? Czytałem na internecie i są różne podejścia.
  2. Posiadam w projekcie (który nie jest projektem na studia, stąd nie umieszczam tutaj kodu) klasę SecurityConfiguration, która definiuje bean'a SecurityFilterChain, który z kolei definiuje autoryzację poprzez Oauth2ResourceServer z wykorzystaniem OpaqueToken. Ponadto jest klasa AuthLogic, która posiada metodę hasAccess() i sprawdza odpowiednią rolę (ZWYKŁY_USER, ADMIN_USER).

W klasie ExampleSerwis:

@PreAuthorize("@authorizationLogic.hasAccess(#xxx, #id)"
List<ExampleDto> findByExampleId(Long id) {
  return repo.findById(id)
}

Ta metoda wołana oczywiście jest potem w kontrolerze.
W jaki sposób podejść do testowania takiej metody pod względem autoryzacji?

Myślałem o teście warstwy webowej. Przykładowy:

@Test
shouldFindExampleDtoById() throws Exception {
  //mock method invocation
  this.mockMvc.perform(get("/examples/{id}", EXAMPLE_ID).with(opaqueToken()).andExpect(status().isOk))
}

Czy taki test ma sens, skoro adnotacja @PreAuthorize (z wywołaniem metody hasAccess() z innego beana) jest na metodzie serwisowej. Ma ktoś doświadczenie w testowaniu takich przypadków?

Z góry dzięki za odpowiedź.

0

Moja rada - skup się na testowaniu zachowania Twojej aplikacji, a test wykorzystania elementów frameworka zostaw na ostatni krok.

I tak każdy test jaki napiszesz będzie po fakcie, więc moim zdaniem powinieneś olać ten temat i pisać testy pod logikę biznesową.

0

Dotychczasowa logika jest otestowana. Doszły rzeczy związane z autoryzacją, które należałoby również zweryfikować.

2
ThygeRRR097 napisał(a):

Dotychczasowa logika jest otestowana. Doszły rzeczy związane z autoryzacją, które należałoby również zweryfikować.

Niby tak, ale testowanie ma sens wtedy kiedy wiesz co robisz. Jak czytam Twój pierwszy post, to to wygląda tak jakbyś się uparł że chcesz dodać to @PreAuthorize, ale tak do końca sam nie wiesz po co. Zastanawiasz się gdzie by tu go dać, i co z nim zrobić.

Nic dobrego nie wynika z podejścia: "Chce dodać coś do kodu", a potem dopiero kminisz "hmm.. jakby to przetestować to co własnie dodałem". Jeśli na prawdę chcesz zrobić dobry test to wywal najpierw tą adnotację. Zastanów się co chciałbyś żeby aplikacja robiła?.

Przykłądowo: kiedy osoba bez jakiegoś uprawnienia chce wyświetlić jakiś zasób, to niech aplikacja zwróci odpowiedni kod:

@Test
void nonAdminShouldNotSeeSecret() {
  loginRegularUser();
  const response = this.mockMvc.perform(get("/secrets"));
  assertEquals(401, response);
}

@Test
void adminShouldSeeSecret() {
  loginAdministratorUser();
  const response = this.mockMvc.perform(get("/secrets"));
  assertEquals(200, response);
}

Oczywiście nie musi to być zwrócenie kodu, może to być co tylko chcesz: np. kiedy osoba bez uprawnień chce odczytać secret mógłbyś np. zwrócić fake secret, odpowiedzieć 200tką i pustym stringiem - co wolisz. Ja dla przykładu zwróciłem kod http 401.

Napisz taki test - on powinien nie przejść. Oczywiście metoda pomocnicza loginRegularUser() powinna zalogować test jako zwykły user a loginAdministratorUser() jako administrator.

Mając taki test, po prostu spraw żeby przeszedł - możesz to zrobić adnotacją, kodem, serwisem, czy jak tam chcesz. Może się okazać że Twój początkowy pomysł z @PreAuthorize będzie dobrym wyjściem, ale możliwe że znajdziesz inne rozwiązanie tego testu, i to też będzie w porząku.

1

Ja, jak już dodaję tę adnotację, to w kontrolerze.

1

Nie powinno się mieszać logiki biznesowej, która zazwyczaj jest właśnie w klasach typu seriws z infrastrukturą, w tym przypadku z security. Jeśli już chcesz użyć tej adnotacji to tak jak mówi @Pinek, w controllerze a potem w teście chyba jakieś WithMockUser.

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.