Skip to content
Snippets Groups Projects
Commit 78583a67 authored by ThanKarab's avatar ThanKarab
Browse files

Cleaning up security configuration code.

parent b7c7f3f0
No related branches found
No related tags found
1 merge request!19Feat/186 experiment refactor
......@@ -43,17 +43,16 @@ WORKDIR /home/portal
ENTRYPOINT ["/run.sh"]
# 8080: Web service API, health checks on http://host:8080$CONTEXT_PATH/health
# 4089: Akka cluster
EXPOSE 4089 8080
EXPOSE 8080
HEALTHCHECK --start-period=60s CMD curl -v --silent http://localhost:8080$CONTEXT_PATH/health 2>&1 | grep UP
LABEL org.label-schema.build-date=$BUILD_DATE \
org.label-schema.name="hbpmip/portal-backend" \
org.label-schema.description="Java backend for the MIP portal" \
org.label-schema.description="Spring backend for the MIP portal" \
org.label-schema.url="https://mip.humanbrainproject.eu" \
org.label-schema.vcs-type="git" \
org.label-schema.vcs-url="https://github.com/LREN-CHUV/portal-backend" \
org.label-schema.vcs-url="https://github.com/HBPMedical/portal-backend" \
org.label-schema.vcs-ref=$VCS_REF \
org.label-schema.version="$VERSION" \
org.label-schema.vendor="LREN CHUV" \
......
......@@ -72,4 +72,6 @@ To use this image, you need a running instance of PostgreSQL and to configure th
* RELEASE_STAGE: Release stage used when reporting errors to Bugsnag. Values are dev, staging, production
* DATA_CENTER_LOCATION: Location of the datacenter, used when reporting errors to Bugsnag
* CONTAINER_ORCHESTRATION: Container orchestration system used to execute the Docker containers. Values are mesos, docker-compose, kubernetes
# TODO Refactor variables
\ No newline at end of file
......@@ -30,26 +30,14 @@ hbp:
client:
clientId: {{ default .Env.CLIENT_ID "996f97c5-a3ca-460e-b18b-00df3e2be89a" }}
clientSecret: {{ .Env.CLIENT_SECRET }}
accessTokenUri: {{ default .Env.TOKEN_URI "https://services.humanbrainproject.eu/oidc/token" }}
userAuthorizationUri: {{ default .Env.AUTH_URI "https://services.humanbrainproject.eu/oidc/authorize" }}
logoutUri: {{ .Env.LOGOUT_URI }}
tokenName: access_token
authenticationScheme: query
clientAuthenticationScheme: form
useCurrentUri: false
preEstablishedRedirectUri: {{ default .Env.FRONTEND_LOGIN_URL "http://frontend/services/login/hbp" }}
resource:
userInfoUri: {{ default .Env.USER_INFO_URI "https://services.humanbrainproject.eu/oidc/userinfo" }}
revokeTokenUri: {{ default .Env.REVOKE_TOKEN_URI "https://services.humanbrainproject.eu/oidc/slo" }}
sso:
login-path:
# WEB FRONTEND
frontend:
loginUrl: {{ default .Env.FRONTEND_LOGIN_URL "http://frontend/services/login/hbp" }}
redirectAfterLoginUrl: {{ default .Env.FRONTEND_AFTER_LOGIN_URL "http://frontend/" }}
redirectAfterLogoutUrl: {{ default .Env.FRONTEND_AFTER_LOGOUT_URL (default .Env.LOGIN_URI "http://frontend/services/login/hbp") }}
redirectAfterLoginUrl: {{ default .Env.FRONTEND_AFTER_LOGIN_URL "http://frontend/home" }}
logging:
level:
root: {{ default .Env.LOG_LEVEL_FRAMEWORK "ERROR" }}
......
package eu.hbp.mip.configuration;
import eu.hbp.mip.model.UserInfo;
import eu.hbp.mip.utils.*;
import eu.hbp.mip.utils.CORSFilter;
import eu.hbp.mip.utils.CustomAccessDeniedHandler;
import eu.hbp.mip.utils.CustomLoginUrlAuthenticationEntryPoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
......@@ -9,14 +10,16 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.security.oauth2.resource.AuthoritiesExtractor;
import org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerProperties;
import org.springframework.boot.autoconfigure.security.oauth2.resource.UserInfoTokenServices;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.*;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.RequestEntity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.oauth2.client.OAuth2ClientContext;
......@@ -57,8 +60,9 @@ import java.util.List;
import java.util.Map;
// See https://spring.io/guides/tutorials/spring-boot-oauth2/ for reference about configuring OAuth2 login
// Reference for OAuth2 login: https://spring.io/guides/tutorials/spring-boot-oauth2/
// also http://cscarioni.blogspot.ch/2013/04/pro-spring-security-and-oauth-2.html
// Security with Keycloak: https://www.thomasvitale.com/keycloak-authentication-flow-sso-client/
@Configuration
@EnableOAuth2Client
......@@ -72,38 +76,40 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
/**
* Enable HBP collab authentication (1) or disable it (0). Default is 1
*/
@Value("#{'${hbp.authentication.enabled:1}'}")
private boolean authentication;
@Value("#{'${hbp.authentication.enabled}'}")
private boolean authenticationEnabled;
/**
* Absolute URL to redirect to when login is required
*/
@Value("#{'${frontend.loginUrl:/login/hbp}'}")
@Value("#{'${frontend.loginUrl}'}")
private String loginUrl;
/**
* Absolute URL to redirect to when logout is required
*/
@Value("#{'${hbp.client.logoutUri}'}")
private String logoutUri;
private String logoutUrl;
/**
* Absolute URL to redirect to after successful login
*/
@Value("#{'${frontend.redirectAfterLoginUrl:http://frontend/home}'}")
@Value("#{'${frontend.redirectAfterLoginUrl}'}")
private String frontendRedirectAfterLogin;
/**
* Absolute URL to redirect to after logout has occurred
* Absolute URL to redirect to after successful logout
*/
@Value("#{'${frontend.redirectAfterLogoutUrl:/login/hbp}'}")
@Value("#{'${frontend.redirectAfterLogoutUrl}'}")
private String redirectAfterLogoutUrl;
/**
* URL to revoke auth token
*/
@Value("#{'${hbp.resource.revokeTokenUri:https://services.humanbrainproject.eu/oidc/revoke}'}")
private String revokeTokenURI;
public boolean getAuthenticationEnabled() {
return authenticationEnabled;
}
public String getFrontendRedirectAfterLogin() {
return frontendRedirectAfterLogin;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
......@@ -111,15 +117,15 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
// @formatter:off
http.addFilterBefore(new CORSFilter(), ChannelProcessingFilter.class);
if (authentication) {
if (authenticationEnabled) {
http.antMatcher("/**")
.authorizeRequests()
.antMatchers(
"/", "/login/**", "/health/**", "/info/**", "/metrics/**", "/trace/**", "/frontend/**", "/webjars/**", "/v2/api-docs", "/swagger-ui.html", "/swagger-resources/**"
)
.permitAll()
"/", "/login/**", "/health/**", "/info/**", "/metrics/**",
"/trace/**", "/frontend/**", "/webjars/**", "/v2/api-docs",
"/swagger-ui.html", "/swagger-resources/**"
).permitAll()
.antMatchers("/galaxy*", "/galaxy/*").hasRole("Data Manager")
//.anyRequest().authenticated()
.anyRequest().hasRole("Researcher")
.and().exceptionHandling().authenticationEntryPoint(new CustomLoginUrlAuthenticationEntryPoint(loginUrl))
.accessDeniedHandler(new CustomAccessDeniedHandler())
......@@ -131,7 +137,8 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
} else {
http.antMatcher("/**")
.authorizeRequests()
.antMatchers("/**").permitAll().and().csrf().disable();
.antMatchers("/**").permitAll()
.and().csrf().disable();
}
}
......@@ -165,13 +172,6 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
return new ResourceServerProperties();
}
public boolean isAuthentication() {
return authentication;
}
public String getFrontendRedirectAfterLogin() {
return frontendRedirectAfterLogin;
}
private Filter csrfHeaderFilter() {
return new OncePerRequestFilter() {
......@@ -199,43 +199,6 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
return repository;
}
private class CustomLogoutHandler implements LogoutHandler {
@Override
public void logout(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) {
// Hackish way of accessing to this information...
final UserInfo userInfo = (UserInfo) httpServletRequest.getSession().getAttribute("userInfo");
if (userInfo != null) {
userInfo.setFakeAuth(false);
}
if (oauth2ClientContext == null || oauth2ClientContext.getAccessToken() == null) {
return;
}
String idToken = oauth2ClientContext.getAccessToken().getAdditionalInformation().get("id_token").toString();
StringBuilder query = new StringBuilder();
query.append("{");
query.append("\"token\":");
query.append("\"").append(idToken).append("\"");
query.append("}");
try {
int responseCode = HTTPUtil.sendPost(revokeTokenURI, query.toString(), new StringBuilder());
if (responseCode != 200) {
LOGGER.warn("Cannot send request to OIDC server for revocation ! ");
} else {
LOGGER.info("Should be logged out");
}
} catch (IOException e) {
LOGGER.warn("Cannot notify logout to OIDC server !");
LOGGER.trace("Cannot notify logout", e);
}
}
}
@Bean
public AuthoritiesExtractor keycloakAuthoritiesExtractor() {
return new KeycloakAuthoritiesExtractor();
......@@ -273,33 +236,31 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
public void logout() {
// POSTするリクエストパラメーターを作成
// TODO Try removing
RestTemplate restTemplate = new RestTemplate();
MultiValueMap<String, String> formParams = new LinkedMultiValueMap<>();
formParams.add("client_id", hbp().getClientId());
formParams.add("client_secret", hbp().getClientSecret());
formParams.add("refresh_token", this.oauth2ClientContext.getAccessToken().getRefreshToken().getValue());
// リクエストヘッダーを作成
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE);
// リクエストを作成
RequestEntity<MultiValueMap<String, String>> requestEntity =
new RequestEntity<>(formParams, httpHeaders, HttpMethod.POST,
URI.create(logoutUri));
// POSTリクエスト送信(ログアウト実行)
ResponseEntity<String> responseEntity = restTemplate.exchange(requestEntity, String.class);
URI.create(logoutUrl));
restTemplate.exchange(requestEntity, String.class);
}
@Value("#{'${services.keycloak.keycloakUrl}'}")
private String keycloakUrl;
// static {
// disableCertificateValidation();
// }
public void disableCertificateValidation() {
//TODO Refactor logging
LOGGER.info("disabling certificate validation host : " + keycloakUrl);
// Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
......@@ -316,18 +277,8 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
// Ignore differences between given hostname and certificate hostname
HostnameVerifier hv = new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
// System.out.println("Warning: URL Host: " + hostname + " vs. "
// + session.getPeerHost());
if (hostname.equals(keycloakUrl) && session.getPeerHost().equals(keycloakUrl)) {
return true;
} else {
return false;
}
}
};
HostnameVerifier hv =
(hostname, session) -> hostname.equals(keycloakUrl) && session.getPeerHost().equals(keycloakUrl);
// Install the all-trusting trust manager
try {
......@@ -336,6 +287,7 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
HttpsURLConnection.setDefaultHostnameVerifier(hv);
} catch (Exception e) {
// TODO add log message
}
}
......
......@@ -55,7 +55,7 @@ public class SecurityApi {
//LOGGER.trace("Cannot read user json", e);
}
if (!securityConfiguration.isAuthentication()) {
if (!securityConfiguration.getAuthenticationEnabled()) {
if (userInfo.isFakeAuth()) {
response.setStatus(401);
}
......@@ -85,7 +85,7 @@ public class SecurityApi {
@ConditionalOnExpression("${hbp.authentication.enabled:0}")
public void noLogin(HttpServletResponse httpServletResponse) throws IOException {
userInfo.setFakeAuth(true);
Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /user/login/hbp", "Unathorized login.");
Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /user/login/hbp", "Unauthorized login.");
httpServletResponse.sendRedirect(securityConfiguration.getFrontendRedirectAfterLogin());
}
......@@ -104,7 +104,6 @@ public class SecurityApi {
*
* @return Return a @{@link ResponseEntity} with the token.
*/
@RequestMapping(path = "/galaxy", method = RequestMethod.GET, produces = "application/json")
@PreAuthorize("hasRole('Data Manager')")
@ResponseStatus(value = HttpStatus.OK)
......
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment