fix: preserve cas timeout semantics

master
chenyuan 4 weeks ago
parent 2e1f695d4c
commit ed58d8a870

@ -6,6 +6,7 @@ import com.yau.digitalrmb.shared.exception.BusinessException;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder; import org.springframework.web.util.UriComponentsBuilder;
import org.w3c.dom.Document; import org.w3c.dom.Document;
@ -26,7 +27,14 @@ public class CasTicketValidator {
@Autowired @Autowired
public CasTicketValidator(PlatformIntegrationProperties properties) { public CasTicketValidator(PlatformIntegrationProperties properties) {
this(properties.getCas(), new RestTemplate()); this(properties.getCas(), createRestTemplate());
}
private static RestTemplate createRestTemplate() {
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setConnectTimeout(5000);
requestFactory.setReadTimeout(5000);
return new RestTemplate(requestFactory);
} }
CasTicketValidator(PlatformIntegrationProperties.Cas properties, RestTemplate restTemplate) { CasTicketValidator(PlatformIntegrationProperties.Cas properties, RestTemplate restTemplate) {

@ -4,6 +4,7 @@ import com.yau.digitalrmb.platformintegration.application.PlatformIdentityReposi
import com.yau.digitalrmb.platformintegration.domain.PlatformActor; import com.yau.digitalrmb.platformintegration.domain.PlatformActor;
import com.yau.digitalrmb.platformintegration.domain.PlatformRole; import com.yau.digitalrmb.platformintegration.domain.PlatformRole;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
@ -65,6 +66,9 @@ public class JdbcPlatformIdentityRepository implements PlatformIdentityRepositor
private Optional<PlatformActor> query(String sql, Object value, PlatformRole role) { private Optional<PlatformActor> query(String sql, Object value, PlatformRole role) {
List<PlatformActor> actors = jdbcTemplate.query(sql, List<PlatformActor> actors = jdbcTemplate.query(sql,
new MapSqlParameterSource("value", value), rowMapper(role)); new MapSqlParameterSource("value", value), rowMapper(role));
if (actors.size() > 1) {
throw new IncorrectResultSizeDataAccessException(1, actors.size());
}
return actors.isEmpty() ? Optional.<PlatformActor>empty() : Optional.of(actors.get(0)); return actors.isEmpty() ? Optional.<PlatformActor>empty() : Optional.of(actors.get(0));
} }

@ -1,12 +1,32 @@
package com.yau.digitalrmb.platformintegration.application; package com.yau.digitalrmb.platformintegration.application;
import com.yau.digitalrmb.platformintegration.config.PlatformIntegrationProperties;
import com.yau.digitalrmb.shared.exception.BusinessException; import com.yau.digitalrmb.shared.exception.BusinessException;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.client.RestTemplate;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import java.lang.reflect.Field;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.Assertions.assertThatThrownBy;
class CasTicketValidatorTest { class CasTicketValidatorTest {
@Test
void configuresFiveSecondTimeoutsForCasRequests() throws Exception {
PlatformIntegrationProperties properties = new PlatformIntegrationProperties();
CasTicketValidator validator = new CasTicketValidator(properties);
Field restTemplateField = ReflectionUtils.findField(CasTicketValidator.class, "restTemplate");
ReflectionUtils.makeAccessible(restTemplateField);
RestTemplate restTemplate = (RestTemplate) ReflectionUtils.getField(restTemplateField, validator);
assertThat(restTemplate.getRequestFactory()).isInstanceOf(SimpleClientHttpRequestFactory.class);
SimpleClientHttpRequestFactory requestFactory = (SimpleClientHttpRequestFactory) restTemplate.getRequestFactory();
assertThat(readIntField(requestFactory, "connectTimeout")).isEqualTo(5000);
assertThat(readIntField(requestFactory, "readTimeout")).isEqualTo(5000);
}
@Test @Test
void parsesSuccessfulCasAccountAndRejectsExternalEntityPayloads() { void parsesSuccessfulCasAccountAndRejectsExternalEntityPayloads() {
String success = "<cas:serviceResponse xmlns:cas=\"http://www.yale.edu/tp/cas\">" String success = "<cas:serviceResponse xmlns:cas=\"http://www.yale.edu/tp/cas\">"
@ -18,4 +38,10 @@ class CasTicketValidatorTest {
assertThat(CasTicketValidator.parseAccount(success)).isEqualTo("t001"); assertThat(CasTicketValidator.parseAccount(success)).isEqualTo("t001");
assertThatThrownBy(() -> CasTicketValidator.parseAccount(xxe)).isInstanceOf(BusinessException.class); assertThatThrownBy(() -> CasTicketValidator.parseAccount(xxe)).isInstanceOf(BusinessException.class);
} }
private int readIntField(Object target, String fieldName) throws Exception {
Field field = ReflectionUtils.findField(target.getClass(), fieldName);
ReflectionUtils.makeAccessible(field);
return (Integer) ReflectionUtils.getField(field, target);
}
} }

@ -6,6 +6,7 @@ import com.yau.digitalrmb.platformintegration.domain.PlatformRole;
import org.h2.jdbcx.JdbcDataSource; import org.h2.jdbcx.JdbcDataSource;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import javax.sql.DataSource; import javax.sql.DataSource;
@ -14,6 +15,7 @@ import java.sql.Statement;
import java.time.Instant; import java.time.Instant;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
class JdbcPlatformIdentityRepositoryTest { class JdbcPlatformIdentityRepositoryTest {
private DataSource dataSource; private DataSource dataSource;
@ -54,6 +56,17 @@ class JdbcPlatformIdentityRepositoryTest {
assertThat(repository.findByPlatformUserId(202L)).isEmpty(); assertThat(repository.findByPlatformUserId(202L)).isEmpty();
} }
@Test
void rejectsDuplicateSchoolAccounts() throws Exception {
execute("INSERT INTO core_user(ID, CODE, NAME, STATE, JOB_TYPE1, DEL_FLAG) VALUES (301, 'duplicate', 'student one', 'S1', 'JT_S_03', 0)");
execute("INSERT INTO core_user(ID, CODE, NAME, STATE, JOB_TYPE1, DEL_FLAG) VALUES (302, 'duplicate', 'student two', 'S1', 'JT_S_03', 0)");
execute("INSERT INTO student(student_id, user_id, student_status, add_time) VALUES (11, 301, 1, '2026-01-01 00:00:00')");
execute("INSERT INTO student(student_id, user_id, student_status, add_time) VALUES (12, 302, 1, '2026-01-01 00:00:00')");
assertThatThrownBy(() -> repository.findBySchoolAccount("duplicate"))
.isInstanceOf(IncorrectResultSizeDataAccessException.class);
}
@Test @Test
private void createSchema() throws Exception { private void createSchema() throws Exception {
execute("CREATE TABLE core_user(ID BIGINT PRIMARY KEY, CODE VARCHAR(64), NAME VARCHAR(64), STATE VARCHAR(16), JOB_TYPE1 VARCHAR(16), DEL_FLAG INT)"); execute("CREATE TABLE core_user(ID BIGINT PRIMARY KEY, CODE VARCHAR(64), NAME VARCHAR(64), STATE VARCHAR(16), JOB_TYPE1 VARCHAR(16), DEL_FLAG INT)");

Loading…
Cancel
Save