Skip to content

Commit

Permalink
Merge pull request #302 from kbss-cvut/development
Browse files Browse the repository at this point in the history
[3.2.0] Release
  • Loading branch information
ledsoft authored Sep 23, 2024
2 parents 45b793f + 2f45d88 commit 22ffaed
Show file tree
Hide file tree
Showing 327 changed files with 10,128 additions and 1,416 deletions.
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#

FROM maven:3-eclipse-temurin-17 as build
FROM maven:3-eclipse-temurin-17 AS build

WORKDIR /termit

Expand All @@ -31,7 +31,7 @@ COPY src src

RUN mvn package -B -P graphdb,standalone -DskipTests=true

FROM eclipse-temurin:17-jdk-alpine as runtime
FROM eclipse-temurin:17-jdk-alpine AS runtime
COPY --from=build /termit/target/termit.jar termit.jar

EXPOSE 8080
Expand Down
37 changes: 31 additions & 6 deletions doc/setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,10 +172,35 @@ termit:

TermIt can operate in two authentication modes:

1. Internal authentication means
2. [Keycloak](https://www.keycloak.org/) -based
1. Internal authentication
2. OAuth2 based (e.g. [Keycloak](https://www.keycloak.org/))

By default, OAuth2 is disabled and internal authentication is used
To enable it, set termit security provider to `oidc`
and provide issuer-uri and jwk-set-uri.

**`application.yml` example:**
```yml
spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: http://keycloak.lan/realms/termit
jwk-set-uri: http://keycloak.lan/realms/termit/protocol/openid-connect/certs
termit:
security:
provider: "oidc"
```
**Environmental variables example:**
```
SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_ISSUERURI=http://keycloak.lan/realms/termit
SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_JWKSETURI=http://keycloak.lan/realms/termit/protocol/openid-connect/certs
TERMIT_SECURITY_PROVIDER=oidc
```

TermIt will automatically configure its security accordingly
(it is using Spring's [`ConditionalOnProperty`](https://www.baeldung.com/spring-conditionalonproperty)).

By default, Keycloak is disabled (see `keycloak.enabled` in `application.yml`). To enable it, set `keycloak.enabled` to `true` and
provide additional required Keycloak parameters - see the [Keycloak Spring Boot integration docs](https://www.keycloak.org/docs/latest/securing_apps/#_spring_boot_adapter).
TermIt will automatically configure its security (it is using Spring's [`ConditionalOnProperty`](https://www.baeldung.com/spring-conditionalonproperty))
accordingly.
**Note that termit-ui needs to be configured for mathcing authentication mode.**
Binary file added doc/throttle-debounce.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
25 changes: 17 additions & 8 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.1</version>
<version>3.3.3</version>
</parent>

<artifactId>termit</artifactId>
<version>3.1.3</version>
<version>3.2.0</version>
<name>TermIt</name>
<description>Terminology manager based on Semantic Web technologies.</description>
<packaging>${packaging}</packaging>
Expand All @@ -29,10 +29,10 @@
<properties>
<java.version>17</java.version>
<org.apache.tika.tika-core.version>2.7.0</org.apache.tika.tika-core.version>
<org.mapstruct.version>1.5.5.Final</org.mapstruct.version>
<org.springdoc.version>2.2.0</org.springdoc.version>
<cz.cvut.kbss.jopa.version>2.0.2</cz.cvut.kbss.jopa.version>
<cz.cvut.kbss.jsonld.version>0.14.3</cz.cvut.kbss.jsonld.version>
<org.mapstruct.version>1.6.0</org.mapstruct.version>
<org.springdoc.version>2.6.0</org.springdoc.version>
<cz.cvut.kbss.jopa.version>2.0.5</cz.cvut.kbss.jopa.version>
<cz.cvut.kbss.jsonld.version>0.15.0</cz.cvut.kbss.jsonld.version>

<!-- Default value for deployment type property which should otherwise specified on command line -->
<deployment>DEV</deployment>
Expand Down Expand Up @@ -112,7 +112,7 @@
<dependency>
<groupId>org.eclipse.rdf4j</groupId>
<artifactId>rdf4j-rio-rdfxml</artifactId>
<version>5.0.0</version>
<version>5.0.2</version>
</dependency>

<!-- Spring declarative transactions with JOPA -->
Expand Down Expand Up @@ -149,6 +149,14 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-messaging</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons</artifactId>
Expand Down Expand Up @@ -286,7 +294,7 @@
<dependency>
<groupId>net.bull.javamelody</groupId>
<artifactId>javamelody-spring-boot-starter</artifactId>
<version>2.0.0</version>
<version>2.2.0</version>
</dependency>

<!-- Caching -->
Expand Down Expand Up @@ -564,6 +572,7 @@
<goal>copy-resources</goal>
</goals>
<configuration>
<propertiesEncoding>ISO-8859-1</propertiesEncoding>
<nonFilteredFileExtensions>
<nonFilteredFileExtension>xlsx</nonFilteredFileExtension>
</nonFilteredFileExtensions>
Expand Down
17 changes: 17 additions & 0 deletions src/main/java/cz/cvut/kbss/termit/config/AppConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,17 @@

import cz.cvut.kbss.termit.util.AsyncExceptionHandler;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.context.annotation.EnableMBeanExport;
import org.springframework.context.annotation.ImportResource;
import org.springframework.context.annotation.aspectj.EnableSpringConfigured;
import org.springframework.retry.annotation.EnableRetry;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;

@Configuration
@EnableMBeanExport
Expand All @@ -35,10 +38,24 @@
@EnableAsync
@EnableScheduling
@EnableRetry
@ImportResource("classpath*:spring-aop.xml")
public class AppConfig implements AsyncConfigurer {

@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new AsyncExceptionHandler();
}

/**
* This thread pool is responsible for executing long-running tasks in the application.
*/
@Bean(destroyMethod = "destroy")
public ThreadPoolTaskScheduler longRunningTaskScheduler(cz.cvut.kbss.termit.util.Configuration config) {
ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
threadPoolTaskScheduler.setPoolSize(config.getAsyncThreadCount());
threadPoolTaskScheduler.setThreadNamePrefix("TermItScheduler-");
threadPoolTaskScheduler.setWaitForTasksToCompleteOnShutdown(true);
threadPoolTaskScheduler.setRemoveOnCancelPolicy(true);
return threadPoolTaskScheduler;
}
}
13 changes: 13 additions & 0 deletions src/main/java/cz/cvut/kbss/termit/config/OAuth2SecurityConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy;
Expand Down Expand Up @@ -84,6 +86,17 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http.build();
}

/**
* Supplies auth provider which is not exposed by HttpSecurity
* @see cz.cvut.kbss.termit.security.WebSocketJwtAuthorizationInterceptor
*/
@Bean
public JwtAuthenticationProvider jwtAuthenticationProvider(JwtDecoder jwtDecoder) {
final JwtAuthenticationProvider provider = new JwtAuthenticationProvider(jwtDecoder);
provider.setJwtAuthenticationConverter(grantedAuthoritiesExtractor());
return provider;
}

private CorsConfigurationSource corsConfigurationSource() {
return SecurityConfig.createCorsConfiguration(config.getCors());
}
Expand Down
30 changes: 28 additions & 2 deletions src/main/java/cz/cvut/kbss/termit/config/SecurityConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import cz.cvut.kbss.termit.security.JwtAuthorizationFilter;
import cz.cvut.kbss.termit.security.JwtUtils;
import cz.cvut.kbss.termit.security.SecurityConstants;
import cz.cvut.kbss.termit.security.TermitJwtDecoder;
import cz.cvut.kbss.termit.service.security.TermItUserDetailsService;
import cz.cvut.kbss.termit.util.Constants;
import org.slf4j.Logger;
Expand All @@ -40,6 +41,10 @@
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider;
import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.HttpStatusEntryPoint;
Expand Down Expand Up @@ -90,7 +95,7 @@ public SecurityConfig(AuthenticationProvider authenticationProvider,
}

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
public SecurityFilterChain filterChain(HttpSecurity http, TermitJwtDecoder jwtDecoder) throws Exception {
LOG.debug("Using internal security mechanisms.");
final AuthenticationManager authManager = buildAuthenticationManager(http);
http.authorizeHttpRequests((auth) -> auth.requestMatchers(antMatcher("/rest/query")).permitAll()
Expand All @@ -102,7 +107,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
.logoutSuccessHandler(authenticationSuccessHandler))
.authenticationManager(authManager)
.addFilter(authenticationFilter(authManager))
.addFilter(new JwtAuthorizationFilter(authManager, jwtUtils, userDetailsService, objectMapper));
.addFilter(new JwtAuthorizationFilter(authManager, jwtUtils, objectMapper, jwtDecoder));
return http.build();
}

Expand All @@ -121,6 +126,22 @@ private JwtAuthenticationFilter authenticationFilter(AuthenticationManager authe
return authenticationFilter;
}

/**
* @see cz.cvut.kbss.termit.security.WebSocketJwtAuthorizationInterceptor
*/
@Bean
public JwtAuthenticationProvider jwtAuthenticationProvider(JwtDecoder jwtDecoder) {
final JwtGrantedAuthoritiesConverter authoritiesConverter = new JwtGrantedAuthoritiesConverter();
authoritiesConverter.setAuthorityPrefix(""); // this removes default "SCOPE_" prefix
// otherwise, all granted authorities would have this prefix
// (like "SCOPE_ROLE_RESTRICTED_USER", we want just ROLE_...)
final JwtAuthenticationConverter converter = new JwtAuthenticationConverter();
converter.setJwtGrantedAuthoritiesConverter(authoritiesConverter);
final JwtAuthenticationProvider provider = new JwtAuthenticationProvider(jwtDecoder);
provider.setJwtAuthenticationConverter(converter);
return provider;
}

private CorsConfigurationSource corsConfigurationSource() {
return createCorsConfiguration(config.getCors());
}
Expand All @@ -143,4 +164,9 @@ protected static CorsConfigurationSource createCorsConfiguration(
source.registerCorsConfiguration("/**", corsConfiguration);
return source;
}

@Bean
public TermitJwtDecoder jwtDecoder() {
return new TermitJwtDecoder(jwtUtils, userDetailsService);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,11 @@
package cz.cvut.kbss.termit.config;

import cz.cvut.kbss.termit.security.SecurityConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import jakarta.servlet.annotation.WebListener;
import jakarta.servlet.http.HttpSessionEvent;
import jakarta.servlet.http.HttpSessionListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@WebListener
public class SessionTimeoutManager implements HttpSessionListener {
Expand Down
45 changes: 24 additions & 21 deletions src/main/java/cz/cvut/kbss/termit/config/WebAppConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
import cz.cvut.kbss.jsonld.jackson.serialization.SerializationConstants;
import cz.cvut.kbss.termit.rest.servlet.DiagnosticsContextFilter;
import cz.cvut.kbss.termit.util.AdjustedUriTemplateProxyServlet;
import cz.cvut.kbss.termit.util.ConfigParam;
import cz.cvut.kbss.termit.util.Constants;
import cz.cvut.kbss.termit.util.json.MultilingualStringDeserializer;
import cz.cvut.kbss.termit.util.json.MultilingualStringSerializer;
Expand Down Expand Up @@ -62,19 +61,13 @@
@Configuration
public class WebAppConfig implements WebMvcConfigurer {

private final cz.cvut.kbss.termit.util.Configuration.Repository config;
private final cz.cvut.kbss.termit.util.Configuration config;

@Value("${application.version:development}")
private String version;

public WebAppConfig(cz.cvut.kbss.termit.util.Configuration config) {
this.config = config.getRepository();
}

@Bean(name = "objectMapper")
@Primary
public ObjectMapper objectMapper() {
return createJsonObjectMapper();
this.config = config;
}

/**
Expand All @@ -99,11 +92,6 @@ public static ObjectMapper createJsonObjectMapper() {
return objectMapper;
}

@Bean(name = "jsonLdMapper")
public ObjectMapper jsonLdObjectMapper() {
return createJsonLdObjectMapper();
}

/**
* Creates an {@link ObjectMapper} for processing JSON-LD using the JB4JSON-LD library.
* <p>
Expand All @@ -119,9 +107,21 @@ public static ObjectMapper createJsonLdObjectMapper() {
jsonLdModule.configure(cz.cvut.kbss.jsonld.ConfigParam.SCAN_PACKAGE, "cz.cvut.kbss.termit");
jsonLdModule.configure(SerializationConstants.FORM, SerializationConstants.FORM_COMPACT_WITH_CONTEXT);
mapper.registerModule(jsonLdModule);
mapper.registerModule(new JavaTimeModule());
return mapper;
}

@Bean(name = "objectMapper")
@Primary
public ObjectMapper objectMapper() {
return createJsonObjectMapper();
}

@Bean(name = "jsonLdMapper")
public ObjectMapper jsonLdObjectMapper() {
return createJsonLdObjectMapper();
}

/**
* Register the proxy for SPARQL endpoint.
*
Expand All @@ -133,10 +133,13 @@ public ServletWrappingController sparqlEndpointController() throws Exception {
controller.setServletClass(AdjustedUriTemplateProxyServlet.class);
controller.setBeanName("sparqlEndpointProxyServlet");
final Properties p = new Properties();
p.setProperty("targetUri", config.getUrl());
final cz.cvut.kbss.termit.util.Configuration.Repository repository = config.getRepository();
p.setProperty("targetUri", repository.getUrl());
p.setProperty("log", "false");
p.setProperty(ConfigParam.REPO_USERNAME.toString(), config.getUsername() != null ? config.getUsername() : "");
p.setProperty(ConfigParam.REPO_PASSWORD.toString(), config.getPassword() != null ? config.getPassword() : "");
p.setProperty(AdjustedUriTemplateProxyServlet.REPO_USERNAME_PARAM,
repository.getUsername() != null ? repository.getUsername() : "");
p.setProperty(AdjustedUriTemplateProxyServlet.REPO_PASSWORD_PARAM,
repository.getPassword() != null ? repository.getPassword() : "");
controller.setInitParameters(p);
controller.afterPropertiesSet();
return controller;
Expand All @@ -153,27 +156,27 @@ public SimpleUrlHandlerMapping sparqlQueryControllerMapping() throws Exception {
}

@Bean
public HttpMessageConverter<?> stringMessageConverter() {
public HttpMessageConverter<?> termitStringHttpMessageConverter() {
return new StringHttpMessageConverter(StandardCharsets.UTF_8);
}

@Bean
public HttpMessageConverter<?> jsonLdMessageConverter() {
public HttpMessageConverter<?> termitJsonLdHttpMessageConverter() {
final MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(
jsonLdObjectMapper());
converter.setSupportedMediaTypes(Collections.singletonList(MediaType.valueOf(JsonLd.MEDIA_TYPE)));
return converter;
}

@Bean
public HttpMessageConverter<?> jsonMessageConverter() {
public HttpMessageConverter<?> termitJsonHttpMessageConverter() {
final MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setObjectMapper(objectMapper());
return converter;
}

@Bean
public HttpMessageConverter<?> resourceMessageConverter() {
public HttpMessageConverter<?> termitResourceHttpMessageConverter() {
return new ResourceHttpMessageConverter();
}

Expand Down
Loading

0 comments on commit 22ffaed

Please sign in to comment.