diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
index 5ec8a3f..22c5dfb 100644
--- a/.github/workflows/release.yaml
+++ b/.github/workflows/release.yaml
@@ -4,6 +4,7 @@ on:
push:
branches:
- main
+ - main-who
jobs:
build:
@@ -32,9 +33,45 @@ jobs:
server-id: docker.io
server-username: DOCKER_USERNAME
server-password: DOCKER_PASSWORD
+ - name: Get current version
+ id: get-version
+ run: |
+ current_version=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)
+ echo "Current version: $current_version"
+ echo "::set-output name=current_version::$current_version"
- name: Bump version
id: bump
- uses: mickem/gh-action-bump-maven-version@v1
+ run: |
+ current_version=${{ steps.get-version.outputs.current_version }}
+ branch=${GITHUB_REF##*/}
+ echo "Current branch: $branch"
+
+ # Extract the base version without suffix
+ base_version=$(echo $current_version | sed -E 's/(-.*)?$//')
+
+ # Increment the base version (assuming semantic versioning)
+ IFS='.' read -r -a version_parts <<< "$base_version"
+ version_parts[2]=$((version_parts[2] + 1))
+ new_base_version="${version_parts[0]}.${version_parts[1]}.${version_parts[2]}"
+
+ if [[ "$branch" == "main-who" ]]; then
+ new_version="${new_base_version}-WHO"
+ else
+ new_version="$new_base_version"
+ fi
+
+ echo "New version: $new_version"
+ mvn versions:set -DnewVersion=$new_version -DgenerateBackupPoms=false
+ echo ":: set-output name=new_version::$new_version"
+ - name: Commit new version
+ run: |
+ git config --global user.name "github-actions[bot]"
+ git config --global user.email "github-actions[bot]@users.noreply.github.com"
+ git add pom.xml
+ git commit -m "Bump version to ${{ steps.bump.outputs.new_version }}"
+ git tag ${{ steps.bump.outputs.new_version }}
+ git push origin HEAD:${GITHUB_REF##*/}
+ git push origin ${{ steps.bump.outputs.new_version }}
- name: Build and Publish package
run: mvn --batch-mode -Prelease package dockerfile:push
- name: Release
@@ -42,9 +79,9 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
- tag_name: ${{ steps.bump.outputs.tag }}
+ tag_name: ${{ steps.bump.outputs.new_version }}
generate_release_notes: true
env:
DOCKER_USERNAME: ${{secrets.DOCKER_USERNAME}}
- DOCKER_TOKEN: ${{secrets.DOCKER_PASSWORD}}
+ DOCKER_TOKEN: ${{secrets.DOCKER_PASSWORD}}
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 2f0fd85..2ae5915 100644
--- a/pom.xml
+++ b/pom.xml
@@ -10,7 +10,7 @@
edu.stanford.protege
webprotege-gwt-api-gateway
- 1.0.11
+ 1.0.12-WHO
webprotege-gwt-api-gateway
The API Gateway for the WebProtégé GWT User Interface
@@ -77,7 +77,7 @@
edu.stanford.protege
webprotege-ipc
- 1.0.4
+ 1.0.5
@@ -153,7 +153,7 @@
edu.stanford.protege
webprotege-backend-api
- 1.0.23
+ 1.0.24-WHO
diff --git a/src/main/java/edu/stanford/protege/webprotege/gateway/FileStorageService.java b/src/main/java/edu/stanford/protege/webprotege/gateway/FileStorageService.java
index ee92be8..d0602ae 100644
--- a/src/main/java/edu/stanford/protege/webprotege/gateway/FileStorageService.java
+++ b/src/main/java/edu/stanford/protege/webprotege/gateway/FileStorageService.java
@@ -43,6 +43,7 @@ public FileSubmissionId storeFile(Path tempFile) {
logger.info("Storing file ({}) in {} bucket with an object id of {}", getFileSizeInMB(tempFile), minioProperties.getBucketName(), fileIdentifier);
createBucketIfNecessary();
uploadObject(tempFile, fileIdentifier);
+ logger.info("File {} uploaded successfully !", fileIdentifier);
return new FileSubmissionId(fileIdentifier);
}
diff --git a/src/main/java/edu/stanford/protege/webprotege/gateway/GatewayController.java b/src/main/java/edu/stanford/protege/webprotege/gateway/GatewayController.java
index 7df42d2..e1f40e4 100644
--- a/src/main/java/edu/stanford/protege/webprotege/gateway/GatewayController.java
+++ b/src/main/java/edu/stanford/protege/webprotege/gateway/GatewayController.java
@@ -1,15 +1,19 @@
package edu.stanford.protege.webprotege.gateway;
import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
import edu.stanford.protege.webprotege.common.UserId;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
+import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.jwt.Jwt;
+import org.springframework.security.web.authentication.logout.LogoutHandler;
+import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@@ -34,8 +38,12 @@ public class GatewayController {
private final RpcRequestProcessor rpcRequestProcessor;
- public GatewayController(RpcRequestProcessor rpcRequestProcessor) {
+ private final LogoutHandler logoutHandler;
+
+
+ public GatewayController(RpcRequestProcessor rpcRequestProcessor, LogoutHandler logoutHandler) {
this.rpcRequestProcessor = rpcRequestProcessor;
+ this.logoutHandler = logoutHandler;
}
@PostMapping(path = "/api/execute", consumes = "application/json")
@@ -64,4 +72,10 @@ public RpcResponse execute(@RequestBody RpcRequest request,
return RpcResponse.forError(request.methodName(), HttpStatus.GATEWAY_TIMEOUT);
}
}
+
+ @GetMapping(path = "/logout")
+ public HttpServletResponse logout(HttpServletRequest request, HttpServletResponse response) {
+ logoutHandler.logout(request, response, SecurityContextHolder.getContext().getAuthentication());
+ return response;
+ }
}
diff --git a/src/main/java/edu/stanford/protege/webprotege/gateway/KeycloakLogoutHandler.java b/src/main/java/edu/stanford/protege/webprotege/gateway/KeycloakLogoutHandler.java
index 5dbc9f6..ae4ab64 100644
--- a/src/main/java/edu/stanford/protege/webprotege/gateway/KeycloakLogoutHandler.java
+++ b/src/main/java/edu/stanford/protege/webprotege/gateway/KeycloakLogoutHandler.java
@@ -8,13 +8,18 @@
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
+import org.springframework.security.oauth2.jwt.Jwt;
+import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
import org.springframework.security.web.authentication.logout.LogoutHandler;
+import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
+import java.net.URI;
+
@Component
-public class KeycloakLogoutHandler implements LogoutHandler {
+public class KeycloakLogoutHandler extends SecurityContextLogoutHandler {
private static final Logger logger = LoggerFactory.getLogger(KeycloakLogoutHandler.class);
private final RestTemplate restTemplate;
@@ -26,21 +31,25 @@ public KeycloakLogoutHandler() {
@Override
public void logout(HttpServletRequest request, HttpServletResponse response,
Authentication auth) {
- logoutFromKeycloak((OidcUser) auth.getPrincipal());
+ logoutFromKeycloak((Jwt) auth.getPrincipal());
+ super.logout(request, response,auth);
}
- private void logoutFromKeycloak(OidcUser user) {
- String endSessionEndpoint = user.getIssuer() + "/protocol/openid-connect/logout";
- UriComponentsBuilder builder = UriComponentsBuilder
- .fromUriString(endSessionEndpoint)
- .queryParam("id_token_hint", user.getIdToken().getTokenValue());
-
- ResponseEntity logoutResponse = restTemplate.getForEntity(
- builder.toUriString(), String.class);
- if (logoutResponse.getStatusCode().is2xxSuccessful()) {
- logger.info("Successfulley logged out from Keycloak");
- } else {
- logger.error("Could not propagate logout to Keycloak");
+ private void logoutFromKeycloak(Jwt token) {
+ String issuer = token.getClaimAsString("iss");
+ String endSessionEndpoint = issuer + "/protocol/openid-connect/logout";
+
+ String accessToken = token.getTokenValue();
+ try {
+ URI logoutUri = new URI(endSessionEndpoint + "?token=" + accessToken);
+ ResponseEntity logoutResponse = restTemplate.getForEntity(logoutUri, String.class);
+ if (logoutResponse.getStatusCode().is2xxSuccessful()) {
+ logger.info("Successfulley logged out from Keycloak");
+ } else {
+ logger.error("Could not propagate logout to Keycloak");
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
}
}
diff --git a/src/main/java/edu/stanford/protege/webprotege/gateway/SecurityConfig.java b/src/main/java/edu/stanford/protege/webprotege/gateway/SecurityConfig.java
index 6f76cfc..4f19efa 100644
--- a/src/main/java/edu/stanford/protege/webprotege/gateway/SecurityConfig.java
+++ b/src/main/java/edu/stanford/protege/webprotege/gateway/SecurityConfig.java
@@ -1,5 +1,6 @@
package edu.stanford.protege.webprotege.gateway;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
@@ -10,9 +11,14 @@
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
import org.springframework.security.core.session.SessionRegistryImpl;
+import org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter;
import org.springframework.security.oauth2.core.oidc.user.OidcUserAuthority;
import org.springframework.security.oauth2.core.user.OAuth2UserAuthority;
import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.security.web.access.intercept.AuthorizationFilter;
+import org.springframework.security.web.authentication.logout.LogoutFilter;
+import org.springframework.security.web.authentication.logout.LogoutHandler;
+import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
@@ -39,16 +45,19 @@ public class SecurityConfig {
SecurityConfig(KeycloakLogoutHandler keycloakLogoutHandler) {
this.keycloakLogoutHandler = keycloakLogoutHandler;
}
-
+ @Bean
+ public LogoutFilter customLogoutFilter() {
+ return new LogoutFilter("/login?logout", keycloakLogoutHandler);
+ }
@Bean
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
}
- @Bean
+/* @Bean
public WebSecurityCustomizer webSecurityCustomizer() {
return (web) -> web.debug(true);
- }
+ }*/
@Bean
public SecurityFilterChain resourceServerFilterChain(HttpSecurity http) throws Exception {
http.csrf(AbstractHttpConfigurer::disable)
@@ -57,10 +66,12 @@ public SecurityFilterChain resourceServerFilterChain(HttpSecurity http) throws E
.anyRequest()
.authenticated()
);
+ http.addFilterAfter(customLogoutFilter(), AuthorizationFilter.class);
http.oauth2ResourceServer((oauth2) -> oauth2
.jwt(Customizer.withDefaults()));
http.oauth2Login(Customizer.withDefaults())
- .logout(logout -> logout.addLogoutHandler(keycloakLogoutHandler).logoutSuccessUrl("/"));
+ .logout(AbstractHttpConfigurer::disable);
+
return http.build();
}
diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml
index 1c62e34..e72233d 100644
--- a/src/main/resources/application.yaml
+++ b/src/main/resources/application.yaml
@@ -1,6 +1,11 @@
server:
port: 7777
+logging:
+ level:
+ org:
+ springframework:
+ amqp: ERROR
spring:
application:
name: webprotege-gwt-api-gateway
@@ -11,15 +16,26 @@ spring:
password: guest
publisher-confirm-type: correlated
publisher-returns: true
+ servlet:
+ multipart:
+ max-file-size: 200MB
+ max-request-size: 200MB
+
webprotege:
rabbitmq:
requestqueue: webprotege-gwt-api-gateway-queue
responsequeue: webprotege-gwt-api-gateway-response-queue
eventsqueue: webprotege-gwt-api-gateway-event-queue
- timeout: 60000
+ timeout: 120000
event-subscribe: true
allowedOrigin: webprotege-local.edu
+ minio:
+ accessKey: webprotege
+ endPoint: http://webprotege-local.edu:9000
+ secretKey: webprotege
+ bucketName: webprotege-uploads
+
spring.security.oauth2:
client: