Public Access
fix(security): refuse the access token of a blocked or deleted user
activeUsers cache, 30 s. Core still accepts the token until it expires; X4 (batch 4) tells Core.
This commit is contained in:
1 parent
509280da14
commit
1f94c87a20
10 files changed
+109
-12
No files matched your search
@@ -7,10 +7,12 @@ import auth_service.shared.security.TokenGenerator;
|
||||
import auth_service.shared.api.AuthCookies;
|
||||
import auth_service.shared.test.AbstractIntegrationTest;
|
||||
import jakarta.servlet.http.Cookie;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.ResultActions;
|
||||
@@ -92,8 +94,21 @@ class RoleCheckFlowTest extends AbstractIntegrationTest {
|
||||
@Autowired
|
||||
private TokenGenerator tokenGenerator;
|
||||
|
||||
@Autowired
|
||||
private JdbcTemplate jdbc;
|
||||
|
||||
private long userId;
|
||||
|
||||
// The filter accepts only the token of an existing, active user.
|
||||
@BeforeEach
|
||||
void seedUser() {
|
||||
jdbc.update("DELETE FROM users WHERE email = 'role-check@example.com'");
|
||||
userId = jdbc.queryForObject("INSERT INTO users (email, password, enabled) VALUES ('role-check@example.com', 'x', true) "
|
||||
+ "RETURNING id", Long.class);
|
||||
}
|
||||
|
||||
private String token(Role role) {
|
||||
return tokenGenerator.generateToken("4711", null, role);
|
||||
return tokenGenerator.generateToken(String.valueOf(userId), null, role);
|
||||
}
|
||||
|
||||
private ResultActions call(String path, Role role) throws Exception {
|
||||
|
||||
@@ -156,6 +156,8 @@ class DeletedAccountFlowTest extends AbstractIntegrationTest {
|
||||
MockHttpServletResponse session = login();
|
||||
String access = session.getCookie(AuthCookies.ACCESS_COOKIE).getValue();
|
||||
String refresh = session.getCookie(AuthCookies.REFRESH_COOKIE).getValue();
|
||||
mockMvc.perform(get("/api/users/me").cookie(new Cookie(AuthCookies.ACCESS_COOKIE, access)))
|
||||
.andExpect(status().isOk());
|
||||
long usersBefore = totalUsers();
|
||||
|
||||
MockHttpServletResponse deleted = deleteAccount(access, PASSWORD).andExpect(status().isNoContent())
|
||||
@@ -168,6 +170,8 @@ class DeletedAccountFlowTest extends AbstractIntegrationTest {
|
||||
.cookie(new Cookie(AuthCookies.REFRESH_COOKIE, refresh)))
|
||||
.andExpect(status().isUnauthorized());
|
||||
assertThat(login().getStatus()).isEqualTo(401);
|
||||
mockMvc.perform(get("/api/users/me").cookie(new Cookie(AuthCookies.ACCESS_COOKIE, access)))
|
||||
.andExpect(status().isUnauthorized());
|
||||
mockMvc.perform(get("/api/public/profiles/" + NAME)).andExpect(status().isNotFound());
|
||||
assertThat(totalUsers()).isEqualTo(usersBefore - 1);
|
||||
}
|
||||
|
||||
@@ -169,9 +169,12 @@ class AdminFlowTest extends AbstractIntegrationTest {
|
||||
void block_EndsTheSessionsAndTheLoginTellsWhy() throws Exception {
|
||||
Session admin = Session.of(login(ADMIN));
|
||||
Session player = Session.of(login(PLAYER));
|
||||
assertThat(me(player)).isEqualTo(200);
|
||||
|
||||
block(admin, playerId, true).andExpect(status().isNoContent());
|
||||
|
||||
assertThat(me(player)).isEqualTo(401);
|
||||
|
||||
assertThat(login(PLAYER).getContentAsString()).contains("ACCOUNT_BLOCKED");
|
||||
assertThat(login(PLAYER).getStatus()).isEqualTo(403);
|
||||
assertThat(mockMvc.perform(post("/api/auth/refresh").header(ClientHeaderFilter.CLIENT_HEADER, "web")
|
||||
@@ -181,11 +184,32 @@ class AdminFlowTest extends AbstractIntegrationTest {
|
||||
|
||||
block(admin, playerId, false).andExpect(status().isNoContent());
|
||||
assertThat(login(PLAYER).getStatus()).isEqualTo(200);
|
||||
assertThat(me(player)).isEqualTo(200);
|
||||
}
|
||||
|
||||
@Test
|
||||
void blockedCookieNextToAValidBearer_UsesTheBearer() throws Exception {
|
||||
Session admin = Session.of(login(ADMIN));
|
||||
Session player = Session.of(login(PLAYER));
|
||||
block(admin, playerId, true).andExpect(status().isNoContent());
|
||||
|
||||
mockMvc.perform(get("/api/users/me")
|
||||
.cookie(new Cookie(AuthCookies.ACCESS_COOKIE, player.access()))
|
||||
.header("Authorization", "Bearer " + admin.access()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.email").value(ADMIN));
|
||||
}
|
||||
|
||||
private int me(Session session) throws Exception {
|
||||
return mockMvc.perform(get("/api/users/me").cookie(new Cookie(AuthCookies.ACCESS_COOKIE, session.access())))
|
||||
.andReturn().getResponse().getStatus();
|
||||
}
|
||||
|
||||
@Test
|
||||
void delete_FlagsTheUser() throws Exception {
|
||||
Session admin = Session.of(login(ADMIN));
|
||||
Session player = Session.of(login(PLAYER));
|
||||
assertThat(me(player)).isEqualTo(200);
|
||||
|
||||
mockMvc.perform(delete("/api/admin/users/" + playerId).header(ClientHeaderFilter.CLIENT_HEADER, "web")
|
||||
.cookie(new Cookie(AuthCookies.ACCESS_COOKIE, admin.access())))
|
||||
@@ -193,6 +217,7 @@ class AdminFlowTest extends AbstractIntegrationTest {
|
||||
|
||||
assertThat(jdbc.queryForObject("SELECT deleted_at FROM users WHERE id = ?", Object.class, playerId)).isNotNull();
|
||||
assertThat(login(PLAYER).getStatus()).isEqualTo(401);
|
||||
assertThat(me(player)).isEqualTo(401);
|
||||
assertThat(audits("DELETE", playerId)).isEqualTo(1);
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user