Skip to content

Commit 2c71c88

Browse files
committed
Merge branch 'release/1.0.2'
2 parents c835240 + a1289a6 commit 2c71c88

31 files changed

+523
-331
lines changed

.github/workflows/workflows.yaml

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,11 @@ jobs:
1313
kubernetes:
1414
- 'v1.21.6'
1515
- 'v1.22.17'
16-
- 'v1.23.15'
17-
- 'v1.24.9'
18-
- 'v1.25.5'
16+
- 'v1.23.17'
17+
- 'v1.24.15'
18+
- 'v1.25.11'
19+
- 'v1.26.6'
20+
- 'v1.27.3'
1921

2022
steps:
2123
- uses: actions/checkout@v2
@@ -32,9 +34,11 @@ jobs:
3234
- name: Setup Minikube
3335
uses: manusa/actions-setup-minikube@v2.7.2
3436
with:
35-
minikube version: 'v1.28.0'
37+
minikube version: 'v1.30.1'
3638
kubernetes version: ${{ matrix.kubernetes }}
3739
github token: ${{ secrets.GITHUB_TOKEN }}
40+
container runtime: containerd
41+
driver: docker
3842
- name: Setup Docker
3943
run: sudo apt-get -qq -y install conntrack socat ; nohup socat TCP-LISTEN:2375,reuseaddr,fork UNIX-CONNECT:/var/run/docker.sock &
4044
- name: Setup Docker Swarm
@@ -52,7 +56,7 @@ jobs:
5256
docker ps -a
5357
kubectl get pod -A
5458
- name: Run Tests
55-
run: mvn -B test
59+
run: mvn -B test
5660

5761
dependency:
5862
runs-on: ubuntu-latest

pom.xml

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
<groupId>eu.openanalytics</groupId>
77
<artifactId>containerproxy</artifactId>
8-
<version>1.0.1</version>
8+
<version>1.0.2</version>
99
<name>ContainerProxy</name>
1010
<packaging>jar</packaging>
1111

@@ -59,14 +59,8 @@
5959
<url>https://clojars.org/repo/</url>
6060
</repository>
6161
<repository>
62-
<!-- Currently used only for the opensaml dependency -->
63-
<id>shibboleth</id>
64-
<url>https://build.shibboleth.net/nexus/content/repositories/releases/</url>
65-
</repository>
66-
<repository>
67-
<!-- Currently used only for the spring-social-github 1.0.0.M4 dependency -->
68-
<id>spring</id>
69-
<url>https://repo.spring.io/plugins-release/</url>
62+
<id>oa-nexus-releases</id>
63+
<url>https://nexus.openanalytics.eu/repository/releases</url>
7064
</repository>
7165
</repositories>
7266

src/main/java/eu/openanalytics/containerproxy/ContainerProxyApplication.java

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import org.apache.logging.log4j.LogManager;
3838
import org.apache.logging.log4j.Logger;
3939
import org.bouncycastle.jce.provider.BouncyCastleProvider;
40+
import org.springdoc.core.GroupedOpenApi;
4041
import org.springframework.beans.factory.annotation.Autowired;
4142
import org.springframework.boot.SpringApplication;
4243
import org.springframework.boot.actuate.health.Health;
@@ -71,12 +72,17 @@
7172
import java.nio.file.Files;
7273
import java.nio.file.Paths;
7374
import java.security.Security;
75+
import java.util.Arrays;
76+
import java.util.HashSet;
7477
import java.util.List;
7578
import java.util.Objects;
7679
import java.util.Properties;
80+
import java.util.Set;
7781
import java.util.concurrent.Executor;
7882

7983
import static eu.openanalytics.containerproxy.api.ApiSecurityService.PROP_API_SECURITY_HIDE_SPEC_DETAILS;
84+
import static eu.openanalytics.containerproxy.service.AppRecoveryService.PROPERTY_RECOVER_RUNNING_PROXIES;
85+
import static eu.openanalytics.containerproxy.service.AppRecoveryService.PROPERTY_RECOVER_RUNNING_PROXIES_FROM_DIFFERENT_CONFIG;
8086
import static eu.openanalytics.containerproxy.service.ProxyService.PROPERTY_STOP_PROXIES_ON_SHUTDOWN;
8187

8288
@EnableScheduling
@@ -119,7 +125,11 @@ public static void main(String[] args) {
119125
app.addListeners(new LoggingConfigurer());
120126

121127
boolean hasExternalConfig = Files.exists(Paths.get(CONFIG_FILENAME));
122-
if (!hasExternalConfig) app.setAdditionalProfiles(CONFIG_DEMO_PROFILE);
128+
if (!hasExternalConfig) {
129+
app.setAdditionalProfiles(CONFIG_DEMO_PROFILE);
130+
Logger log = LogManager.getLogger(ContainerProxyApplication.class);
131+
log.warn("WARNING: Did not found configuration, using fallback configuration!");
132+
}
123133

124134
setDefaultProperties(app);
125135

@@ -152,7 +162,6 @@ public void init() {
152162
log.warn("WARNING: Invalid configuration detected: same-site-cookie policy is set to None, but secure-cookies are not enabled. Secure cookies must be enabled when using None as same-site-cookie policy ");
153163
}
154164

155-
156165
if (environment.getProperty("proxy.store-mode", "").equalsIgnoreCase("Redis")) {
157166
if (!environment.getProperty("spring.session.store-type", "").equalsIgnoreCase("redis")) {
158167
// running in HA mode, but not using Redis sessions
@@ -162,6 +171,24 @@ public void init() {
162171
// running in HA mode, but proxies are removed when shutting down
163172
log.warn("WARNING: Invalid configuration detected: store-mode is set to Redis (i.e. High-Availability mode), but proxies are stopped at shutdown of server!");
164173
}
174+
if (environment.getProperty( PROPERTY_RECOVER_RUNNING_PROXIES, Boolean.class, false) ||
175+
environment.getProperty( PROPERTY_RECOVER_RUNNING_PROXIES_FROM_DIFFERENT_CONFIG, Boolean.class, false) ) {
176+
log.warn("WARNING: Invalid configuration detected: cannot use store-mode with Redis (i.e. High-Availability mode) and app recovery at the same time. Disable app recovery!");
177+
}
178+
}
179+
180+
if (environment.getProperty("spring.session.store-type", "").equalsIgnoreCase("redis")) {
181+
if (!environment.getProperty("proxy.store-mode", "").equalsIgnoreCase("Redis")) {
182+
// using Redis sessions, but not running in HA mode -> this does not make sense
183+
// even with one replica, the HA mode should be used in order for the server to survive restarts (which is the reason Redis sessions are used)
184+
log.warn("WARNING: Invalid configuration detected: user sessions are stored in Redis, but store-more is not set to Redis. Change store-mode so that app sessions are stored in Redis!");
185+
}
186+
if (environment.getProperty( PROPERTY_RECOVER_RUNNING_PROXIES, Boolean.class, false) ||
187+
environment.getProperty( PROPERTY_RECOVER_RUNNING_PROXIES_FROM_DIFFERENT_CONFIG, Boolean.class, false) ) {
188+
// using Redis sessions together with app recovery -> this does not make sense
189+
// if already using Redis for sessions there is no reason to not store app sessions
190+
log.warn("WARNING: Invalid configuration detected: user sessions are stored in Redis and App Recovery is enabled. Instead of using App Recovery, change store-mode so that app sessions are stored in Redis!");
191+
}
165192
}
166193

167194
boolean hideSpecDetails = environment.getProperty(PROP_API_SECURITY_HIDE_SPEC_DETAILS, Boolean.class, true);
@@ -345,4 +372,24 @@ private static void setDefaultProperties(SpringApplication app) {
345372
System.setProperty("jdk.serialSetFilterAfterRead", "true");
346373
}
347374

375+
@Bean
376+
public GroupedOpenApi groupOpenApi() {
377+
return GroupedOpenApi.builder()
378+
.group("v1")
379+
.addOpenApiCustomiser(openApi -> {
380+
Set<String> endpoints = new HashSet<>(Arrays.asList("/app_direct_i/**", "/app_direct/**", "/app_proxy/{proxyId}/**", "/error"));
381+
openApi.getPaths().entrySet().stream().filter(p -> endpoints.contains(p.getKey()))
382+
.forEach(p -> {
383+
p.getValue().setHead(null);
384+
p.getValue().setPost(null);
385+
p.getValue().setDelete(null);
386+
p.getValue().setParameters(null);
387+
p.getValue().setOptions(null);
388+
p.getValue().setPut(null);
389+
p.getValue().setPatch(null);
390+
});
391+
})
392+
.build();
393+
}
394+
348395
}

src/main/java/eu/openanalytics/containerproxy/api/ProxyRouteController.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import eu.openanalytics.containerproxy.model.runtime.Proxy;
2424
import eu.openanalytics.containerproxy.service.ProxyService;
2525
import eu.openanalytics.containerproxy.service.UserService;
26+
import eu.openanalytics.containerproxy.util.ImmediateJsonResponse;
2627
import eu.openanalytics.containerproxy.util.ProxyMappingManager;
2728
import eu.openanalytics.containerproxy.util.ContextPathHelper;
2829
import org.springframework.stereotype.Controller;

src/main/java/eu/openanalytics/containerproxy/api/ProxyStatusController.java

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,10 @@ public class ProxyStatusController {
8080
mediaType = "application/json",
8181
schema = @Schema(implementation = ChangeProxyStatusDto.class),
8282
examples = {
83-
@ExampleObject(name = "Stopping", description = "Stop a proxy.", value = "{\"desiredStatus\": \"Stopping\"}"),
84-
@ExampleObject(name = "Pausing", description = "Pause a proxy.", value = "{\"desiredStatus\": \"Pausing\"}"),
85-
@ExampleObject(name = "Resuming", description = "Resume a proxy.", value = "{\"desiredStatus\": \"Resuming\"}"),
86-
@ExampleObject(name = "Resuming with parameters", description = "Resume a proxy.", value = "{\"desiredStatus\": \"Resuming\", \"parameters\":{\"resources\":\"2 CPU cores - 8G RAM\",\"other_parameter\":\"example\"}}")
83+
@ExampleObject(name = "Stopping", description = "Stop a proxy.", value = "{\"desiredState\": \"Stopping\"}"),
84+
@ExampleObject(name = "Pausing", description = "Pause a proxy.", value = "{\"desiredState\": \"Pausing\"}"),
85+
@ExampleObject(name = "Resuming", description = "Resume a proxy.", value = "{\"desiredState\": \"Resuming\"}"),
86+
@ExampleObject(name = "Resuming with parameters", description = "Resume a proxy.", value = "{\"desiredState\": \"Resuming\", \"parameters\":{\"resources\":\"2 CPU cores - 8G RAM\",\"other_parameter\":\"example\"}}")
8787
}
8888
)
8989
)
@@ -140,15 +140,12 @@ public ResponseEntity<ApiResponse<Void>> changeProxyStatus(@PathVariable String
140140
return ApiResponse.fail(ex.getMessage());
141141
}
142142
} else if (changeProxyStateDto.getDesiredState().equals("Stopping")) {
143-
if (!proxy.getStatus().equals(ProxyStatus.New)
144-
&& !proxy.getStatus().equals(ProxyStatus.Up)
145-
&& !proxy.getStatus().equals(ProxyStatus.Resuming)
146-
&& !proxy.getStatus().equals(ProxyStatus.Paused)) {
147-
return ApiResponse.fail(String.format("Cannot stop proxy because it is not in New, Up or Paused status (status is %s)", proxy.getStatus()));
143+
if (proxy.getStatus().equals(ProxyStatus.Stopped)) {
144+
return ApiResponse.fail("Cannot stop proxy because it is already stopped");
148145
}
149146
asyncProxyService.stopProxy(proxy, false);
150147
} else {
151-
return ApiResponse.fail("Invalid desiredStatus");
148+
return ApiResponse.fail("Invalid desiredState");
152149
}
153150

154151
return ApiResponse.success();

src/main/java/eu/openanalytics/containerproxy/auth/impl/KeycloakAuthenticationBackend.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@
5454
import org.springframework.security.core.GrantedAuthority;
5555
import org.springframework.security.core.session.SessionRegistryImpl;
5656
import org.springframework.security.web.AuthenticationEntryPoint;
57-
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
57+
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
5858
import org.springframework.security.web.authentication.logout.LogoutFilter;
5959
import org.springframework.security.web.authentication.session.ChangeSessionIdAuthenticationStrategy;
6060
import org.springframework.security.web.authentication.session.CompositeSessionAuthenticationStrategy;
@@ -92,6 +92,10 @@ public class KeycloakAuthenticationBackend implements IAuthenticationBackend {
9292
@Lazy
9393
AuthenticationManager authenticationManager;
9494

95+
@Inject
96+
@Lazy
97+
private SavedRequestAwareAuthenticationSuccessHandler successHandler;
98+
9599
@Override
96100
public String getName() {
97101
return NAME;
@@ -142,9 +146,7 @@ protected KeycloakAuthenticationProcessingFilter keycloakAuthenticationProcessin
142146
KeycloakAuthenticationProcessingFilter filter = new KeycloakAuthenticationProcessingFilter(authenticationManager, requestMatcher);
143147
filter.setSessionAuthenticationStrategy(sessionAuthenticationStrategy());
144148
filter.setAuthenticationFailureHandler(keycloakAuthenticationFailureHandler());
145-
SimpleUrlAuthenticationSuccessHandler handler = new SimpleUrlAuthenticationSuccessHandler("/");
146-
handler.setAlwaysUseDefaultTargetUrl(true);
147-
filter.setAuthenticationSuccessHandler(handler);
149+
filter.setAuthenticationSuccessHandler(successHandler);
148150
// Fix: call afterPropertiesSet manually, because Spring doesn't invoke it for some reason.
149151
filter.setApplicationContext(ctx);
150152
filter.afterPropertiesSet();

src/main/java/eu/openanalytics/containerproxy/auth/impl/OpenIDAuthenticationBackend.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.apache.logging.log4j.LogManager;
3232
import org.apache.logging.log4j.Logger;
3333
import org.springframework.beans.factory.annotation.Autowired;
34+
import org.springframework.context.annotation.Lazy;
3435
import org.springframework.core.env.Environment;
3536
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
3637
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
@@ -58,6 +59,7 @@
5859
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
5960
import org.springframework.security.oauth2.core.oidc.user.OidcUserAuthority;
6061
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
62+
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
6163
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
6264
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
6365
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
@@ -92,6 +94,10 @@ public class OpenIDAuthenticationBackend implements IAuthenticationBackend {
9294
@Inject
9395
private ClientRegistrationRepository clientRegistrationRepo;
9496

97+
@Inject
98+
@Lazy
99+
private SavedRequestAwareAuthenticationSuccessHandler successHandler;
100+
95101
private static OAuth2AuthorizedClientService oAuth2AuthorizedClientService;
96102

97103
@Autowired
@@ -115,11 +121,11 @@ public boolean hasAuthorization() {
115121
@Override
116122
public void configureHttpSecurity(HttpSecurity http, AuthorizedUrl anyRequestConfigurer) throws Exception {
117123
anyRequestConfigurer.authenticated();
118-
124+
119125
http
120126
.oauth2Login()
121127
.loginPage("/login")
122-
.defaultSuccessUrl("/", true)
128+
.successHandler(successHandler)
123129
.clientRegistrationRepository(clientRegistrationRepo)
124130
.authorizedClientService(oAuth2AuthorizedClientService)
125131
.authorizationEndpoint()
@@ -140,7 +146,7 @@ public void onAuthenticationFailure(HttpServletRequest request, HttpServletRespo
140146
.oidcUserService(createOidcUserService())
141147
.and()
142148
.and()
143-
.addFilterAfter(openIdReAuthorizeFilter, UsernamePasswordAuthenticationFilter.class);
149+
.addFilterAfter(openIdReAuthorizeFilter, UsernamePasswordAuthenticationFilter.class);
144150
}
145151

146152
private OAuth2AuthorizationRequestResolver authorizationRequestResolver() {

src/main/java/eu/openanalytics/containerproxy/auth/impl/SAMLAuthenticationBackend.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import eu.openanalytics.containerproxy.util.ContextPathHelper;
2727
import org.springframework.beans.factory.annotation.Autowired;
2828
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
29+
import org.springframework.context.annotation.Lazy;
2930
import org.springframework.core.env.Environment;
3031
import org.springframework.security.authentication.ProviderManager;
3132
import org.springframework.security.config.annotation.ObjectPostProcessor;
@@ -40,6 +41,7 @@
4041
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;
4142
import org.springframework.security.saml2.provider.service.servlet.filter.Saml2WebSsoAuthenticationFilter;
4243
import org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutRequestResolver;
44+
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
4345
import org.springframework.security.web.authentication.logout.LogoutFilter;
4446
import org.springframework.security.web.util.matcher.AndRequestMatcher;
4547
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
@@ -55,6 +57,7 @@
5557
import static eu.openanalytics.containerproxy.auth.impl.saml.SAMLConfiguration.SAML_LOGOUT_SERVICE_LOCATION_PATH;
5658
import static eu.openanalytics.containerproxy.auth.impl.saml.SAMLConfiguration.SAML_LOGOUT_SERVICE_RESPONSE_LOCATION_PATH;
5759
import static eu.openanalytics.containerproxy.auth.impl.saml.SAMLConfiguration.SAML_SERVICE_LOCATION_PATH;
60+
import static eu.openanalytics.containerproxy.ui.AuthController.AUTH_SUCCESS_URL;
5861

5962
@Component
6063
@ConditionalOnProperty(name = "proxy.authentication", havingValue = "saml")
@@ -75,6 +78,10 @@ public class SAMLAuthenticationBackend implements IAuthenticationBackend {
7578
@Inject
7679
private Saml2LogoutRequestResolver saml2LogoutRequestResolver;
7780

81+
@Inject
82+
@Lazy
83+
private SavedRequestAwareAuthenticationSuccessHandler successHandler;
84+
7885
@Override
7986
public String getName() {
8087
return NAME;
@@ -98,7 +105,7 @@ public void configureHttpSecurity(HttpSecurity http, AuthorizedUrl anyRequestCon
98105
.loginProcessingUrl(SAML_SERVICE_LOCATION_PATH)
99106
.authenticationManager(new ProviderManager(samlAuthenticationProvider))
100107
.failureHandler(failureHandler)
101-
.defaultSuccessUrl("/", true))
108+
.successHandler(successHandler))
102109
.saml2Logout(saml -> saml
103110
.logoutUrl(SAML_LOGOUT_SERVICE_LOCATION_PATH)
104111
.logoutResponse(r -> r.logoutUrl(SAML_LOGOUT_SERVICE_RESPONSE_LOCATION_PATH))

src/main/java/eu/openanalytics/containerproxy/auth/impl/SocialAuthenticationBackend.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
*/
2121
package eu.openanalytics.containerproxy.auth.impl;
2222

23+
import org.slf4j.Logger;
24+
import org.slf4j.LoggerFactory;
2325
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
2426
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
2527
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer.AuthorizedUrl;
@@ -33,6 +35,15 @@
3335
public class SocialAuthenticationBackend implements IAuthenticationBackend {
3436

3537
public static final String NAME = "social";
38+
39+
public SocialAuthenticationBackend() {
40+
Logger logger = LoggerFactory.getLogger(getClass());
41+
logger.warn("WARNING: ###");
42+
logger.warn("WARNING: ###");
43+
logger.warn("WARNING: Social authentication is deprecated and will be removed in the next version (3.1.0)!");
44+
logger.warn("WARNING: ###");
45+
logger.warn("WARNING: ###");
46+
}
3647

3748
@Override
3849
public String getName() {

0 commit comments

Comments
 (0)