Skip to content

Commit

Permalink
Add security and an actuator endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
flawmop committed Aug 20, 2024
1 parent 340e303 commit f61246b
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 5 deletions.
10 changes: 6 additions & 4 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ configurations {
}

dependencies {
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-web'
// Note: Introduces commons-logging conflict with spring-boot-starter-web!
implementation 'org.springframework.cloud:spring-cloud-stream-binder-rabbit'
Expand Down Expand Up @@ -61,15 +63,15 @@ testing {
}
}

i(JvmTestSuite) {
test_i(JvmTestSuite) {
sources {
java {
srcDirs = ['src/test-i/java']
}
}
}

e2e(JvmTestSuite) {
test_e2e(JvmTestSuite) {
sources {
java {
srcDirs = ['src/test-e2e/java']
Expand Down Expand Up @@ -100,8 +102,8 @@ tasks.register('codeCoverageReport', JacocoReport) {

tasks.named('check') {
dependsOn(test)
dependsOn(testing.suites.i)
dependsOn(testing.suites.e2e)
dependsOn(testing.suites.test_i)
dependsOn(testing.suites.test_e2e)
dependsOn('codeCoverageReport')
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package com.insilicosoft.portal.svc.rip.config;

import static org.springframework.security.config.Customizer.withDefaults;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest;
import org.springframework.boot.actuate.info.InfoEndpoint;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;

import com.insilicosoft.portal.svc.rip.RipIdentifiers;

/**
* Security configuration.
*
* @author geoff
*/
@Configuration
public class SecurityConfig {

private String actuatorUsername;
private String actuatorPassword;

/**
* Initialising constructor.
*
* @param actuatorUsername Actuator endpoint username.
* @param actuatorPassword Actuator endpoint password.
*/
public SecurityConfig(@Value("${com.insilicosoft.actuator.username}") String actuatorUsername,
@Value("${com.insilicosoft.actuator.password}") String actuatorPassword) {
this.actuatorUsername = actuatorUsername;
this.actuatorPassword = actuatorPassword;
}

@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// Note: We've got MVC in classpath, so mvcMatchers (not antMatchers) are in effect
http.authorizeHttpRequests((authz) -> authz.requestMatchers(EndpointRequest.to(InfoEndpoint.class)).authenticated())
.httpBasic(withDefaults());
return http.build();
}

// Bypass security completely
@Bean
WebSecurityCustomizer webSecurityCustomizer() {
return (web) -> web.ignoring().requestMatchers(RipIdentifiers.REQUEST_MAPPING_RUN.concat("/**"));
}

@Bean
PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}

@Bean
InMemoryUserDetailsManager userDetailsService(PasswordEncoder passwordEncoder) {
UserDetails user = User.withUsername(actuatorUsername)
.password(passwordEncoder.encode(actuatorPassword)).build();
return new InMemoryUserDetailsManager(user);
}
}
12 changes: 11 additions & 1 deletion src/main/resources/application.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
com:
insilicosoft:
actuator:
username: actuator-user
password: actuator-password
executor:
core-pool-size: 2
max-pool-size: 2
Expand All @@ -10,6 +13,13 @@ logging:
level:
root: DEBUG

management:
endpoints:
web:
exposure:
include:
- info

server:
netty:
connection-timeout: 2s
Expand Down Expand Up @@ -39,4 +49,4 @@ spring:
# Max size for a multipart/form-data request (inc. file uploads)
max-request-size: 1MB
# Files stored in memory up to this amount (thereafter stored on disk).
file-size-threshold: 250KB
file-size-threshold: 250KB
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.insilicosoft.portal.svc.rip.actuator;

import static org.assertj.core.api.Assertions.assertThat;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;

@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class ActuatorE2E {

private String actuatorUsername;
private String actuatorPassword;

@Autowired
private TestRestTemplate restTemplate;

public ActuatorE2E(@Value("${com.insilicosoft.actuator.username}") String actuatorUsername,
@Value("${com.insilicosoft.actuator.password}") String actuatorPassword) {
this.actuatorUsername = actuatorUsername;
this.actuatorPassword = actuatorPassword;
}

public
@Test
void test() {
ResponseEntity<String> response = restTemplate.getForEntity("/actuator/info", String.class);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
}

@Test
void test2() {
ResponseEntity<String> response = restTemplate.withBasicAuth(actuatorUsername, actuatorPassword)
.getForEntity("/actuator/info", String.class);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(response.getBody()).isEqualTo("{}");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,16 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;

import com.insilicosoft.portal.svc.rip.RipIdentifiers;
import com.insilicosoft.portal.svc.rip.config.SecurityConfig;
import com.insilicosoft.portal.svc.rip.service.InputProcessorService;

@WebMvcTest(FileAsyncUploadController.class)
@Import(SecurityConfig.class)
public class FileAsyncUploadControllerIT {

private static final MediaType textWithCharset = new MediaType(MediaType.TEXT_PLAIN, StandardCharsets.UTF_8);
Expand Down

0 comments on commit f61246b

Please sign in to comment.