Skip to content

Commit 515489c

Browse files
committed
feat(server, ui): Fix PR
1 parent 1f55d11 commit 515489c

21 files changed

+294
-208
lines changed

chutney/packaging/local-dev/src/main/resources/application-sso-auth.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ spring:
99
client-id: my-client
1010
client-secret: my-client-secret
1111
authorization-grant-type: authorization_code
12-
redirect-uri: "https://localhost:4200/login/oauth2/code/{registrationId}"
12+
redirect-uri: "https://${server.http.interface}:${server.port}/login/oauth2/code/{registrationId}"
1313
scope: openid, profile, email
1414
client-name: My Provider
1515
provider:

chutney/packaging/local-dev/src/main/resources/application.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@ spring:
5555
- ldap
5656
- mem-auth
5757
- db-sqlite
58-
- sso-auth
5958

6059
chutney:
6160
configuration-folder: .chutney/conf
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2017-2024 Enedis
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*
6+
*/
7+
8+
package com.chutneytesting.security;
9+
10+
import com.chutneytesting.admin.api.InfoController;
11+
import com.chutneytesting.security.api.SsoOpenIdConnectController;
12+
import com.chutneytesting.security.api.UserController;
13+
import com.chutneytesting.security.api.UserDto;
14+
import com.chutneytesting.security.domain.SsoOpenIdConnectConfigService;
15+
import com.chutneytesting.security.infra.handlers.Http401FailureHandler;
16+
import com.chutneytesting.security.infra.handlers.HttpEmptyLogoutSuccessHandler;
17+
import com.chutneytesting.security.infra.handlers.HttpLoginSuccessHandler;
18+
import com.chutneytesting.security.infra.sso.SsoOpenIdConnectConfigProperties;
19+
import com.chutneytesting.server.core.domain.security.Authorization;
20+
import com.chutneytesting.server.core.domain.security.User;
21+
import java.util.ArrayList;
22+
import org.springframework.beans.factory.annotation.Value;
23+
import org.springframework.context.annotation.Bean;
24+
import org.springframework.http.HttpStatus;
25+
import org.springframework.lang.Nullable;
26+
import org.springframework.security.config.Customizer;
27+
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
28+
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
29+
import org.springframework.security.config.annotation.web.configurers.ChannelSecurityConfigurer;
30+
import org.springframework.security.web.authentication.HttpStatusEntryPoint;
31+
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
32+
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
33+
34+
public abstract class AbstractChutneyWebSecurityConfig {
35+
36+
protected static final String LOGIN_URL = UserController.BASE_URL + "/login";
37+
protected static final String LOGOUT_URL = UserController.BASE_URL + "/logout";
38+
protected static final String API_BASE_URL_PATTERN = "/api/**";
39+
40+
@Bean
41+
public SsoOpenIdConnectConfigService ssoOpenIdConnectConfigService(@Nullable SsoOpenIdConnectConfigProperties ssoOpenIdConnectConfigProperties) {
42+
return new SsoOpenIdConnectConfigService(ssoOpenIdConnectConfigProperties);
43+
}
44+
45+
@Value("${management.endpoints.web.base-path:/actuator}")
46+
protected String actuatorBaseUrl;
47+
48+
@Value("${server.ssl.enabled:true}")
49+
private Boolean sslEnabled;
50+
51+
protected HttpSecurity configureHttp(final HttpSecurity http) throws Exception {
52+
configureBaseHttpSecurity(http);
53+
UserDto anonymous = anonymous();
54+
http
55+
.anonymous(anonymousConfigurer -> anonymousConfigurer
56+
.principal(anonymous)
57+
.authorities(new ArrayList<>(anonymous.getAuthorities())))
58+
.authorizeHttpRequests(httpRequest -> {
59+
HandlerMappingIntrospector introspector = new HandlerMappingIntrospector();
60+
httpRequest
61+
.requestMatchers(new MvcRequestMatcher(introspector, LOGIN_URL)).permitAll()
62+
.requestMatchers(new MvcRequestMatcher(introspector, LOGOUT_URL)).permitAll()
63+
.requestMatchers(new MvcRequestMatcher(introspector, InfoController.BASE_URL + "/**")).permitAll()
64+
.requestMatchers(new MvcRequestMatcher(introspector, SsoOpenIdConnectController.BASE_URL + "/**")).permitAll()
65+
.requestMatchers(new MvcRequestMatcher(introspector, API_BASE_URL_PATTERN)).authenticated()
66+
.requestMatchers(new MvcRequestMatcher(introspector, actuatorBaseUrl + "/**")).hasAuthority(Authorization.ADMIN_ACCESS.name())
67+
.anyRequest().permitAll();
68+
})
69+
.httpBasic(Customizer.withDefaults());
70+
return http;
71+
}
72+
73+
protected void configureBaseHttpSecurity(final HttpSecurity http) throws Exception {
74+
http
75+
.csrf(AbstractHttpConfigurer::disable)
76+
.exceptionHandling(httpSecurityExceptionHandlingConfigurer -> httpSecurityExceptionHandlingConfigurer.authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED)))
77+
.requiresChannel(this.requireChannel(sslEnabled))
78+
.formLogin(httpSecurityFormLoginConfigurer -> httpSecurityFormLoginConfigurer
79+
.loginProcessingUrl(LOGIN_URL)
80+
.successHandler(new HttpLoginSuccessHandler())
81+
.failureHandler(new Http401FailureHandler()))
82+
.logout(httpSecurityLogoutConfigurer -> httpSecurityLogoutConfigurer
83+
.logoutUrl(LOGOUT_URL)
84+
.logoutSuccessHandler(new HttpEmptyLogoutSuccessHandler()));
85+
}
86+
87+
protected UserDto anonymous() {
88+
UserDto anonymous = new UserDto();
89+
anonymous.setId(User.ANONYMOUS.id);
90+
anonymous.setName(User.ANONYMOUS.id);
91+
anonymous.grantAuthority("ANONYMOUS");
92+
return anonymous;
93+
}
94+
95+
private Customizer<ChannelSecurityConfigurer<HttpSecurity>.ChannelRequestMatcherRegistry> requireChannel(Boolean sslEnabled) {
96+
if (sslEnabled) {
97+
return channelRequestMatcherRegistry -> channelRequestMatcherRegistry.anyRequest().requiresSecure();
98+
} else {
99+
return channelRequestMatcherRegistry -> channelRequestMatcherRegistry.anyRequest().requiresInsecure();
100+
}
101+
}
102+
}

chutney/server/src/main/java/com/chutneytesting/security/ChutneyWebSecurityConfig.java

Lines changed: 2 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -7,119 +7,33 @@
77

88
package com.chutneytesting.security;
99

10-
11-
import com.chutneytesting.admin.api.InfoController;
12-
import com.chutneytesting.security.api.SsoOpenIdConnectController;
13-
import com.chutneytesting.security.api.UserController;
14-
import com.chutneytesting.security.api.UserDto;
1510
import com.chutneytesting.security.domain.AuthenticationService;
1611
import com.chutneytesting.security.domain.Authorizations;
17-
import com.chutneytesting.security.infra.handlers.Http401FailureHandler;
18-
import com.chutneytesting.security.infra.handlers.HttpEmptyLogoutSuccessHandler;
19-
import com.chutneytesting.security.infra.handlers.HttpLoginSuccessHandler;
20-
import com.chutneytesting.security.infra.sso.SsoOpenIdConnectConfig;
21-
import com.chutneytesting.server.core.domain.security.Authorization;
22-
import com.chutneytesting.server.core.domain.security.User;
23-
import java.util.ArrayList;
24-
import org.springframework.beans.factory.annotation.Value;
2512
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
2613
import org.springframework.context.annotation.Bean;
2714
import org.springframework.context.annotation.Configuration;
2815
import org.springframework.context.annotation.Profile;
2916
import org.springframework.core.annotation.Order;
30-
import org.springframework.http.HttpStatus;
31-
import org.springframework.security.config.Customizer;
3217
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
3318
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
3419
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
35-
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
36-
import org.springframework.security.config.annotation.web.configurers.ChannelSecurityConfigurer;
3720
import org.springframework.security.web.SecurityFilterChain;
38-
import org.springframework.security.web.authentication.HttpStatusEntryPoint;
39-
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
40-
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
4121

4222
@Configuration
4323
@EnableWebSecurity
4424
@EnableMethodSecurity
4525
@Profile("!sso-auth")
46-
public class ChutneyWebSecurityConfig {
47-
48-
public static final String LOGIN_URL = UserController.BASE_URL + "/login";
49-
public static final String LOGOUT_URL = UserController.BASE_URL + "/logout";
50-
public static final String API_BASE_URL_PATTERN = "/api/**";
51-
52-
@Value("${management.endpoints.web.base-path:/actuator}")
53-
public static String ACTUATOR_BASE_URL;
54-
55-
@Value("${server.ssl.enabled:true}")
56-
Boolean sslEnabled;
26+
public class ChutneyWebSecurityConfig extends AbstractChutneyWebSecurityConfig {
5727

5828
@Bean
5929
public AuthenticationService authenticationService(Authorizations authorizations) {
6030
return new AuthenticationService(authorizations);
6131
}
6232

63-
@Bean
64-
@ConditionalOnMissingBean
65-
public SsoOpenIdConnectConfig emptySsoOpenIdConnectConfig() {
66-
return new SsoOpenIdConnectConfig();
67-
}
68-
69-
7033
@Bean
7134
@Order()
7235
@ConditionalOnMissingBean(value = SecurityFilterChain.class)
7336
public SecurityFilterChain securityFilterChain(final HttpSecurity http) throws Exception {
74-
configureBaseHttpSecurity(http, sslEnabled);
75-
UserDto anonymous = anonymous();
76-
http
77-
.anonymous(anonymousConfigurer -> anonymousConfigurer
78-
.principal(anonymous)
79-
.authorities(new ArrayList<>(anonymous.getAuthorities())))
80-
.authorizeHttpRequests(httpRequest -> {
81-
HandlerMappingIntrospector introspector = new HandlerMappingIntrospector();
82-
httpRequest
83-
.requestMatchers(new MvcRequestMatcher(introspector, LOGIN_URL)).permitAll()
84-
.requestMatchers(new MvcRequestMatcher(introspector, LOGOUT_URL)).permitAll()
85-
.requestMatchers(new MvcRequestMatcher(introspector, InfoController.BASE_URL + "/**")).permitAll()
86-
.requestMatchers(new MvcRequestMatcher(introspector, SsoOpenIdConnectController.BASE_URL)).permitAll()
87-
.requestMatchers(new MvcRequestMatcher(introspector, SsoOpenIdConnectController.BASE_URL + "/**")).permitAll()
88-
.requestMatchers(new MvcRequestMatcher(introspector, API_BASE_URL_PATTERN)).authenticated()
89-
.requestMatchers(new MvcRequestMatcher(introspector, ACTUATOR_BASE_URL + "/**")).hasAuthority(Authorization.ADMIN_ACCESS.name())
90-
.anyRequest().permitAll();
91-
})
92-
.httpBasic(Customizer.withDefaults());
93-
return http.build();
94-
}
95-
96-
public void configureBaseHttpSecurity(final HttpSecurity http, Boolean sslEnabled) throws Exception {
97-
http
98-
.csrf(AbstractHttpConfigurer::disable)
99-
.exceptionHandling(httpSecurityExceptionHandlingConfigurer -> httpSecurityExceptionHandlingConfigurer.authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED)))
100-
.requiresChannel(this.requireChannel(sslEnabled))
101-
.formLogin(httpSecurityFormLoginConfigurer -> httpSecurityFormLoginConfigurer
102-
.loginProcessingUrl(LOGIN_URL)
103-
.successHandler(new HttpLoginSuccessHandler())
104-
.failureHandler(new Http401FailureHandler()))
105-
.logout(httpSecurityLogoutConfigurer -> httpSecurityLogoutConfigurer
106-
.logoutUrl(LOGOUT_URL)
107-
.logoutSuccessHandler(new HttpEmptyLogoutSuccessHandler()));
108-
}
109-
110-
public UserDto anonymous() {
111-
UserDto anonymous = new UserDto();
112-
anonymous.setId(User.ANONYMOUS.id);
113-
anonymous.setName(User.ANONYMOUS.id);
114-
anonymous.grantAuthority("ANONYMOUS");
115-
return anonymous;
116-
}
117-
118-
private Customizer<ChannelSecurityConfigurer<HttpSecurity>.ChannelRequestMatcherRegistry> requireChannel(Boolean sslEnabled) {
119-
if (sslEnabled) {
120-
return channelRequestMatcherRegistry -> channelRequestMatcherRegistry.anyRequest().requiresSecure();
121-
} else {
122-
return channelRequestMatcherRegistry -> channelRequestMatcherRegistry.anyRequest().requiresInsecure();
123-
}
37+
return configureHttp(http).build();
12438
}
12539
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2017-2024 Enedis
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*
6+
*/
7+
8+
package com.chutneytesting.security.api;
9+
10+
public class SsoOpenIdConnectConfigDto {
11+
public final String issuer;
12+
public final String clientId;
13+
public final String clientSecret;
14+
public final String responseType;
15+
public final String scope;
16+
public final String redirectBaseUrl;
17+
public final String ssoProviderName;
18+
public final Boolean oidc;
19+
20+
public SsoOpenIdConnectConfigDto(String issuer, String clientId, String clientSecret, String responseType, String scope, String redirectBaseUrl, String ssoProviderName, Boolean oidc) {
21+
this.issuer = issuer;
22+
this.clientId = clientId;
23+
this.clientSecret = clientSecret;
24+
this.responseType = responseType;
25+
this.scope = scope;
26+
this.redirectBaseUrl = redirectBaseUrl;
27+
this.ssoProviderName = ssoProviderName;
28+
this.oidc = oidc;
29+
}
30+
}

chutney/server/src/main/java/com/chutneytesting/security/api/SsoOpenIdConnectController.java

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77

88
package com.chutneytesting.security.api;
99

10-
import com.chutneytesting.security.infra.sso.SsoOpenIdConnectConfig;
10+
import static com.chutneytesting.security.api.SsoOpenIdConnectMapper.toDto;
11+
12+
import com.chutneytesting.security.domain.SsoOpenIdConnectConfigService;
1113
import org.springframework.http.MediaType;
1214
import org.springframework.web.bind.annotation.CrossOrigin;
1315
import org.springframework.web.bind.annotation.GetMapping;
@@ -21,14 +23,17 @@ public class SsoOpenIdConnectController {
2123

2224
public static final String BASE_URL = "/api/v1/sso";
2325

24-
private final SsoOpenIdConnectConfig ssoOpenIdConnectConfig;
26+
private final SsoOpenIdConnectConfigService ssoOpenIdConnectConfigService;
2527

26-
SsoOpenIdConnectController(SsoOpenIdConnectConfig ssoOpenIdConnectConfig) {
27-
this.ssoOpenIdConnectConfig = ssoOpenIdConnectConfig;
28+
SsoOpenIdConnectController(SsoOpenIdConnectConfigService ssoOpenIdConnectConfigService) {
29+
this.ssoOpenIdConnectConfigService = ssoOpenIdConnectConfigService;
2830
}
2931

3032
@GetMapping(path = "/config", produces = MediaType.APPLICATION_JSON_VALUE)
31-
public SsoOpenIdConnectConfig getLastCampaignExecution() {
32-
return ssoOpenIdConnectConfig;
33+
public SsoOpenIdConnectConfigDto getSsoOpenIdConnectConfig() {
34+
if (ssoOpenIdConnectConfigService == null) {
35+
return null;
36+
}
37+
return toDto(ssoOpenIdConnectConfigService.getSsoOpenIdConnectConfig());
3338
}
3439
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2017-2024 Enedis
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*
6+
*/
7+
8+
package com.chutneytesting.security.api;
9+
10+
public class SsoOpenIdConnectMapper {
11+
public static SsoOpenIdConnectConfigDto toDto(com.chutneytesting.security.domain.SsoOpenIdConnectConfig ssoOpenIdConnectConfig) {
12+
if (ssoOpenIdConnectConfig == null) {
13+
return null;
14+
}
15+
return new SsoOpenIdConnectConfigDto(
16+
ssoOpenIdConnectConfig.issuer,
17+
ssoOpenIdConnectConfig.clientId,
18+
ssoOpenIdConnectConfig.clientSecret,
19+
ssoOpenIdConnectConfig.responseType,
20+
ssoOpenIdConnectConfig.scope,
21+
ssoOpenIdConnectConfig.redirectBaseUrl,
22+
ssoOpenIdConnectConfig.ssoProviderName,
23+
ssoOpenIdConnectConfig.oidc
24+
);
25+
}
26+
}

chutney/server/src/main/java/com/chutneytesting/security/infra/sso/SsoOpenIdConnectConfig.java renamed to chutney/server/src/main/java/com/chutneytesting/security/domain/SsoOpenIdConnectConfig.java

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,9 @@
55
*
66
*/
77

8-
package com.chutneytesting.security.infra.sso;
8+
package com.chutneytesting.security.domain;
99

1010
public class SsoOpenIdConnectConfig {
11-
1211
public final String issuer;
1312
public final String clientId;
1413
public final String clientSecret;
@@ -18,17 +17,6 @@ public class SsoOpenIdConnectConfig {
1817
public final String ssoProviderName;
1918
public final Boolean oidc;
2019

21-
public SsoOpenIdConnectConfig() {
22-
this.issuer = null;
23-
this.clientId = null;
24-
this.clientSecret = null;
25-
this.responseType = null;
26-
this.scope = null;
27-
this.redirectBaseUrl = null;
28-
this.ssoProviderName = null;
29-
this.oidc = null;
30-
}
31-
3220
public SsoOpenIdConnectConfig(String issuer, String clientId, String clientSecret, String responseType, String scope, String redirectBaseUrl, String ssoProviderName, Boolean oidc) {
3321
this.issuer = issuer;
3422
this.clientId = clientId;
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2017-2024 Enedis
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*
6+
*/
7+
8+
package com.chutneytesting.security.domain;
9+
10+
import static com.chutneytesting.security.domain.SsoOpenIdConnectMapper.toDomain;
11+
12+
import com.chutneytesting.security.infra.sso.SsoOpenIdConnectConfigProperties;
13+
14+
public class SsoOpenIdConnectConfigService {
15+
16+
private final SsoOpenIdConnectConfigProperties ssoOpenIdConnectConfigProperties;
17+
18+
public SsoOpenIdConnectConfigService(SsoOpenIdConnectConfigProperties ssoOpenIdConnectConfigProperties) {
19+
this.ssoOpenIdConnectConfigProperties = ssoOpenIdConnectConfigProperties;
20+
}
21+
22+
public SsoOpenIdConnectConfig getSsoOpenIdConnectConfig() {
23+
return toDomain(ssoOpenIdConnectConfigProperties);
24+
}
25+
}

0 commit comments

Comments
 (0)