Public Access
feat(profile): store the public profile look
This commit is contained in:
1 parent
f86f3c2e63
commit
dd4fcd1546
27 files changed
+308
-15
No files matched your search
@@ -110,7 +110,7 @@ class JwtServiceTest extends AbstractUnitTest {
|
||||
|
||||
@Test
|
||||
void generateProfileTicket_NamesTheUserAndExpiresSoon() {
|
||||
var ticket = jwtService.generateProfileTicket(42L, List.of());
|
||||
var ticket = jwtService.generateProfileTicket(42L, List.of(), List.of());
|
||||
|
||||
var claims = Jwts.parser().verifyWith(publicKey).build().parseSignedClaims(ticket).getPayload();
|
||||
assertEquals("public-profile", claims.get("typ", String.class));
|
||||
@@ -122,15 +122,16 @@ class JwtServiceTest extends AbstractUnitTest {
|
||||
|
||||
@Test
|
||||
void generateProfileTicket_CarriesTheSharedStatGroups() {
|
||||
String ticket = jwtService.generateProfileTicket(42L, List.of("PILE", "TREND"));
|
||||
String ticket = jwtService.generateProfileTicket(42L, List.of("PILE", "TREND"), List.of("PLAYING", "STATS"));
|
||||
|
||||
Claims claims = Jwts.parser().verifyWith(publicKey).build().parseSignedClaims(ticket).getPayload();
|
||||
assertEquals(List.of("PILE", "TREND"), claims.get("profileStats", List.class));
|
||||
assertEquals(List.of("PLAYING", "STATS"), claims.get("sections", List.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void parseLoginToken_ProfileTicketIsNoLoginToken() {
|
||||
assertEquals(Optional.empty(), jwtService.parseLoginToken(jwtService.generateProfileTicket(42L, List.of())));
|
||||
assertEquals(Optional.empty(), jwtService.parseLoginToken(jwtService.generateProfileTicket(42L, List.of(), List.of())));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -84,6 +84,40 @@ class PublicProfileControllerTest extends AbstractIntegrationTest {
|
||||
assertEquals(List.of("PILE", "TREND"), claims.get("profileStats", List.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void publicProfile_AnswersWithTheOwnersStyle() throws Exception {
|
||||
jdbc.update("update user_settings set profile_accent = 'red', profile_corners = 'round', profile_banner_appid = 620, "
|
||||
+ "profile_sections = '{PLAYING,SUMMARY}' where user_id = ?", publicUserId);
|
||||
|
||||
mockMvc.perform(get("/api/public/profiles/Lobo"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.style.accent").value("red"))
|
||||
.andExpect(jsonPath("$.style.corners").value("round"))
|
||||
.andExpect(jsonPath("$.style.bannerAppid").value(620))
|
||||
.andExpect(jsonPath("$.style.backgroundAppid").isEmpty())
|
||||
.andExpect(jsonPath("$.style.sections[0]").value("PLAYING"))
|
||||
.andExpect(jsonPath("$.style.sections[1]").value("SUMMARY"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void publicProfile_NewRowShowsEverySection() throws Exception {
|
||||
mockMvc.perform(get("/api/public/profiles/Lobo"))
|
||||
.andExpect(jsonPath("$.style.accent").value("violet"))
|
||||
.andExpect(jsonPath("$.style.sections.length()").value(5));
|
||||
}
|
||||
|
||||
@Test
|
||||
void publicProfile_TicketCarriesTheShownSections() throws Exception {
|
||||
jdbc.update("update user_settings set profile_sections = '{PLAYING}' where user_id = ?", publicUserId);
|
||||
|
||||
String body = mockMvc.perform(get("/api/public/profiles/Lobo")).andReturn().getResponse().getContentAsString();
|
||||
|
||||
String ticket = body.replaceAll(".*\"ticket\"\\s*:\\s*\"([^\"]+)\".*", "$1");
|
||||
PublicKey key = KeyFactory.getInstance("Ed25519").generatePublic(new X509EncodedKeySpec(Base64.getDecoder().decode(publicKey)));
|
||||
Claims claims = Jwts.parser().verifyWith(key).build().parseSignedClaims(ticket).getPayload();
|
||||
assertEquals(List.of("PLAYING"), claims.get("sections", List.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void publicProfile_NameIgnoresCase() throws Exception {
|
||||
mockMvc.perform(get("/api/public/profiles/lOBO"))
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package auth_service.modules.user.api;
|
||||
|
||||
import auth_service.modules.user.model.ProfileStyle;
|
||||
import auth_service.configuration.security.JwtAuthenticationFilter;
|
||||
import auth_service.configuration.security.JwtService;
|
||||
import auth_service.shared.security.UserPrincipal;
|
||||
@@ -390,6 +391,62 @@ class UserControllerTest extends AbstractWebMvcTest {
|
||||
.andExpect(status().isBadRequest());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getCurrentUserV1_ShowsTheProfileStyle() throws Exception {
|
||||
String email = "test@example.com";
|
||||
UserSettings settings = UserSettings.builder().language("en").theme("dark")
|
||||
.profileStyle(new ProfileStyle("red", "sharp", 620L, null, List.of("PLAYING"))).build();
|
||||
SecurityContextHolder.getContext().setAuthentication(
|
||||
new UsernamePasswordAuthenticationToken(new UserPrincipal("1", email, null), null, Collections.emptyList())
|
||||
);
|
||||
when(userService.getUserByEmail(email)).thenReturn(User.builder().email(email).settings(settings).build());
|
||||
|
||||
mockMvc.perform(get("/api/users/me").accept(MediaType.APPLICATION_JSON))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.settings.profileStyle.accent").value("red"))
|
||||
.andExpect(jsonPath("$.settings.profileStyle.corners").value("sharp"))
|
||||
.andExpect(jsonPath("$.settings.profileStyle.bannerAppid").value(620))
|
||||
.andExpect(jsonPath("$.settings.profileStyle.backgroundAppid").isEmpty())
|
||||
.andExpect(jsonPath("$.settings.profileStyle.sections[0]").value("PLAYING"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void updateProfileStyle_StoresTheValues() throws Exception {
|
||||
SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(
|
||||
new UserPrincipal("1", "test@example.com", null), null, Collections.emptyList()));
|
||||
|
||||
mockMvc.perform(put("/api/users/settings/profile-style")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("{\"accent\":\"green\",\"corners\":\"round\",\"bannerAppid\":620,"
|
||||
+ "\"sections\":[\"STATS\",\"SUMMARY\"]}"))
|
||||
.andExpect(status().isNoContent());
|
||||
|
||||
verify(userService).updateProfileStyle(1L, new ProfileStyle("green", "round", 620L, null, List.of("STATS", "SUMMARY")));
|
||||
}
|
||||
|
||||
// Each body differs from a valid one in one field only, so the 400 comes from that field's constraint.
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {
|
||||
"{\"accent\":\"hotpink\",\"corners\":\"default\",\"sections\":[]}",
|
||||
"{\"accent\":\"violet\",\"corners\":\"pointy\",\"sections\":[]}",
|
||||
"{\"accent\":\"violet\",\"corners\":\"default\",\"bannerAppid\":0,\"sections\":[]}",
|
||||
"{\"accent\":\"violet\",\"corners\":\"default\",\"backgroundAppid\":-620,\"sections\":[]}",
|
||||
"{\"accent\":\"violet\",\"corners\":\"default\",\"sections\":[\"NOTES\"]}",
|
||||
"{\"accent\":\"violet\",\"corners\":\"default\",\"sections\":[null]}",
|
||||
"{\"accent\":\"violet\",\"corners\":\"default\"}"
|
||||
})
|
||||
void updateProfileStyle_UnknownOrMissingValue_IsRejected(String body) throws Exception {
|
||||
SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(
|
||||
new UserPrincipal("1", "test@example.com", null), null, Collections.emptyList()));
|
||||
|
||||
mockMvc.perform(put("/api/users/settings/profile-style")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(body))
|
||||
.andExpect(status().isBadRequest());
|
||||
|
||||
verifyNoInteractions(userService);
|
||||
}
|
||||
|
||||
@Test
|
||||
void updateAppearance_StoresTheValues() throws Exception {
|
||||
SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package auth_service.modules.user.model;
|
||||
|
||||
import auth_service.shared.test.AbstractUnitTest;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
class ProfileStyleTest extends AbstractUnitTest {
|
||||
|
||||
@Test
|
||||
void keepsEachSectionOnceAtItsFirstPlace() {
|
||||
assertEquals(List.of("STATS", "PLAYING"),
|
||||
new ProfileStyle("red", "round", 620L, null, List.of("STATS", "PLAYING", "STATS")).sections());
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package auth_service.modules.user.persistence;
|
||||
|
||||
import auth_service.modules.user.model.ProfileStyle;
|
||||
import auth_service.modules.user.exception.UserNotFoundException;
|
||||
import auth_service.modules.user.model.Appearance;
|
||||
import auth_service.modules.user.model.SteamIdHolder;
|
||||
@@ -280,6 +281,26 @@ class UserRepositoryTest extends AbstractPersistenceTest {
|
||||
.getSettings().getAppearance());
|
||||
}
|
||||
|
||||
@Test
|
||||
void updateProfileStyle_IsReadBackAndStartsWithTheDefaults() {
|
||||
UserEntity userEntity = entityManager.persist(UserEntity.builder()
|
||||
.email("style@example.com")
|
||||
.password("password")
|
||||
.enabled(true)
|
||||
.registrationDate(LocalDateTime.now())
|
||||
.build());
|
||||
entityManager.flush();
|
||||
assertEquals(ProfileStyle.DEFAULT, userRepository.findByEmail("style@example.com").orElseThrow()
|
||||
.getSettings().getProfileStyle());
|
||||
|
||||
ProfileStyle style = new ProfileStyle("pink", "sharp", 620L, 1086940L, List.of("FINISHED", "SUMMARY"));
|
||||
userRepository.updateProfileStyle(userEntity.getId(), style);
|
||||
entityManager.flush();
|
||||
entityManager.clear();
|
||||
|
||||
assertEquals(style, userRepository.findByEmail("style@example.com").orElseThrow().getSettings().getProfileStyle());
|
||||
}
|
||||
|
||||
@Test
|
||||
void updateProfileStats_IsReadBackAndKeptByUpdateSettings() {
|
||||
UserEntity userEntity = entityManager.persist(UserEntity.builder()
|
||||
|
||||
Reference in new issue
Block a user