From c3d889c92d8eacfd93bab2dbc2e9a4db61d9aae7 Mon Sep 17 00:00:00 2001 From: Vitaly Takmazov Date: Fri, 20 Mar 2020 15:18:29 +0300 Subject: enable spring boot actuator for admin users --- build.gradle | 1 + .../juick/server/configuration/SecurityConfig.java | 1 + src/main/java/com/juick/service/UserService.java | 2 ++ .../java/com/juick/service/UserServiceImpl.java | 10 +++++++ .../service/security/JuickUserDetailsService.java | 7 ++++- .../juick/service/security/entities/JuickUser.java | 4 ++- src/main/resources/application.properties | 2 ++ .../java/com/juick/server/tests/ServerTests.java | 32 ++++++++++++++++++++-- 8 files changed, 54 insertions(+), 5 deletions(-) diff --git a/build.gradle b/build.gradle index e9ced0f3..3b4af0ef 100644 --- a/build.gradle +++ b/build.gradle @@ -121,6 +121,7 @@ dependencies { compile ("org.springframework.boot:spring-boot-starter-security") compile ("org.springframework.boot:spring-boot-starter-web") compile ("org.springframework.boot:spring-boot-starter-json") + compile ("org.springframework.boot:spring-boot-starter-actuator") compile ('org.springframework.boot:spring-boot-devtools') compile 'org.flywaydb:flyway-core:6.3.1' diff --git a/src/main/java/com/juick/server/configuration/SecurityConfig.java b/src/main/java/com/juick/server/configuration/SecurityConfig.java index 2713cc56..23a41e11 100644 --- a/src/main/java/com/juick/server/configuration/SecurityConfig.java +++ b/src/main/java/com/juick/server/configuration/SecurityConfig.java @@ -189,6 +189,7 @@ public class SecurityConfig { .authorizeRequests() .antMatchers("/settings", "/pm/**", "/**/bl", "/_twitter", "/post", "/post2", "/comment") .authenticated() + .antMatchers("/actuator/**").hasRole("ADMIN") .anyRequest().permitAll() .and() .anonymous().principal(JuickUser.ANONYMOUS_USER).authorities(JuickUser.ANONYMOUS_AUTHORITY) diff --git a/src/main/java/com/juick/service/UserService.java b/src/main/java/com/juick/service/UserService.java index e171126d..4bd5486d 100644 --- a/src/main/java/com/juick/service/UserService.java +++ b/src/main/java/com/juick/service/UserService.java @@ -127,4 +127,6 @@ public interface UserService { List getActiveJIDs(); void updateLastSeen(User user); + + boolean isAdminUser(User user); } diff --git a/src/main/java/com/juick/service/UserServiceImpl.java b/src/main/java/com/juick/service/UserServiceImpl.java index 084f3a00..23c55bbe 100644 --- a/src/main/java/com/juick/service/UserServiceImpl.java +++ b/src/main/java/com/juick/service/UserServiceImpl.java @@ -24,6 +24,7 @@ import com.juick.model.AuthResponse; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Value; import org.springframework.dao.DuplicateKeyException; import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.jdbc.core.RowMapper; @@ -39,6 +40,7 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.sql.Timestamp; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -52,6 +54,9 @@ import java.util.UUID; @Repository public class UserServiceImpl extends BaseJdbcService implements UserService { + @Value("${juick.admin_users:}") + List adminUsers; + private class UserMapper implements RowMapper { @Override public User mapRow(@Nonnull ResultSet rs, int rowNum) throws SQLException { @@ -681,4 +686,9 @@ public class UserServiceImpl extends BaseJdbcService implements UserService { public void updateLastSeen(User user) { getJdbcTemplate().update("UPDATE users SET last_seen=now() WHERE id=?", user.getUid()); } + + @Override + public boolean isAdminUser(User user) { + return adminUsers.contains(user.getName()); + } } diff --git a/src/main/java/com/juick/service/security/JuickUserDetailsService.java b/src/main/java/com/juick/service/security/JuickUserDetailsService.java index a62bdadd..da222539 100644 --- a/src/main/java/com/juick/service/security/JuickUserDetailsService.java +++ b/src/main/java/com/juick/service/security/JuickUserDetailsService.java @@ -21,11 +21,14 @@ import com.juick.model.User; import com.juick.service.UserService; import com.juick.service.security.entities.JuickUser; import org.apache.commons.lang3.StringUtils; +import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.util.Assert; +import java.util.List; + /** * Created by aalexeev on 11/28/16. */ @@ -46,7 +49,9 @@ public class JuickUserDetailsService implements UserDetailsService { if (!user.isAnonymous()) { user.setAuthHash(userService.getHashByUID(user.getUid())); - return new JuickUser(user); + List authorities = userService.isAdminUser(user) ? + JuickUser.ADMIN_AUTHORITY : JuickUser.USER_AUTHORITY; + return new JuickUser(user, authorities); } throw new UsernameNotFoundException("The username " + username + " is not found"); diff --git a/src/main/java/com/juick/service/security/entities/JuickUser.java b/src/main/java/com/juick/service/security/entities/JuickUser.java index 062db3a4..dca5fe7d 100644 --- a/src/main/java/com/juick/service/security/entities/JuickUser.java +++ b/src/main/java/com/juick/service/security/entities/JuickUser.java @@ -17,8 +17,8 @@ package com.juick.service.security.entities; -import com.juick.model.User; import com.juick.model.AnonymousUser; +import com.juick.model.User; import org.apache.commons.lang3.StringUtils; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; @@ -33,10 +33,12 @@ import java.util.List; */ public class JuickUser implements UserDetails { static final GrantedAuthority ROLE_USER = new SimpleGrantedAuthority("ROLE_USER"); + static final GrantedAuthority ROLE_ADMIN = new SimpleGrantedAuthority("ROLE_ADMIN"); static final GrantedAuthority ROLE_ANONYMOUS = new SimpleGrantedAuthority("ROLE_ANONYMOUS"); public static final List USER_AUTHORITY = Collections.singletonList(ROLE_USER); public static final List ANONYMOUS_AUTHORITY = Collections.singletonList(ROLE_ANONYMOUS); + public static final List ADMIN_AUTHORITY = List.of(ROLE_ADMIN, ROLE_USER); public static final JuickUser ANONYMOUS_USER = new JuickUser(AnonymousUser.INSTANCE, ANONYMOUS_AUTHORITY); diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index f0a6d03a..4909dfce 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -4,3 +4,5 @@ spring.jackson.serialization.write-dates-as-timestamps=false spring.jackson.serialization.write-empty-json-arrays=true spring.h2.console.enabled=true spring.datasource.platform=h2 +management.endpoints.web.exposure.include=* +management.security.roles=ROLE_ADMIN diff --git a/src/test/java/com/juick/server/tests/ServerTests.java b/src/test/java/com/juick/server/tests/ServerTests.java index ebfcda08..32db8544 100644 --- a/src/test/java/com/juick/server/tests/ServerTests.java +++ b/src/test/java/com/juick/server/tests/ServerTests.java @@ -161,7 +161,8 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT) @TestPropertySource(properties = { - "ios_app_id=12345678.com.juick.ExampleApp" + "ios_app_id=12345678.com.juick.ExampleApp", + "juick.admin_users=ugnich" }) @AutoConfigureMockMvc public class ServerTests { @@ -2385,8 +2386,8 @@ public class ServerTests { ECPublicKeySpec pubSpec = new ECPublicKeySpec(point, ecSpec); ECPublicKey publicKeyGenerated = (ECPublicKey) keyFactory.generatePublic(pubSpec); - Jws jwt = Jwts.parser() - .setSigningKey(publicKeyGenerated) + Jws jwt = Jwts.parserBuilder() + .setSigningKey(publicKeyGenerated).build() .parseClaimsJws(secret); Assert.assertThat(jwt.getHeader().get("kid"), is("keyid")); Assert.assertThat(jwt.getHeader().get("alg"), is("ES256")); @@ -2395,4 +2396,29 @@ public class ServerTests { Assert.assertThat(claims.get("sub"), is("com.example.app")); Assert.assertThat(claims.get("aud"), is("https://appleid.apple.com")); } + + @Test + public void adminsTest() throws Exception { + assertThat(userService.isAdminUser(ugnich), is(true)); + assertThat(userService.isAdminUser(freefd), is(false)); + MvcResult formLoginResult = mockMvc.perform(post("/login") + .param("username", ugnichName) + .param("password", ugnichPassword)) + .andExpect(status().is3xxRedirection()).andReturn(); + Cookie ugnichLogin = formLoginResult.getResponse().getCookie("juick-remember-me"); + + formLoginResult = mockMvc.perform(post("/login") + .param("username", freefdName) + .param("password", freefdPassword)) + .andExpect(status().is3xxRedirection()).andReturn(); + Cookie freefdLogin = formLoginResult.getResponse().getCookie("juick-remember-me"); + + mockMvc.perform(get("/actuator/health") + .cookie(ugnichLogin)) + .andExpect(status().isOk()); + mockMvc.perform(get("/actuator/health") + .cookie(freefdLogin)) + .andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrl("http://localhost/login")); + } } -- cgit v1.2.3