Skip to content
Snippets Groups Projects
Commit 2b708492 authored by jerrypan44's avatar jerrypan44
Browse files

WIP committed working features roles and keycloak integration

parent 0f2f8bf0
No related branches found
No related tags found
2 merge requests!7Features/keycloak integration,!6Features/keycloak integration
This commit is part of merge request !6. Comments created here will be created in the context of that merge request.
......@@ -30,11 +30,11 @@ hbp:
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" }}
tokenName: oauth_token
tokenName: access_token
authenticationScheme: query
clientAuthenticationScheme: form
useCurrentUri: false
preEstablishedRedirectUri: {{ default .Env.FRONTEND_LOGIN_URL "http://frontend/services/login/hbp" }}
preEstablishedRedirectUri: {{ default .Env.FRONTEND_LOGIN_URL "http://localhost:8080/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" }}
......@@ -52,6 +52,7 @@ logging:
root: {{ default .Env.LOG_LEVEL "INFO" }}
org:
springframework:
security: DEBUG
web: {{ default .Env.LOGGING_LEVEL_WEB "WARN" }}
web.servlet.handler.BeanNameUrlHandlerMapping: WARN
hibernate: {{ default .Env.LOGGING_LEVEL_HIBERNATE "WARN" }}
......
......@@ -255,12 +255,6 @@
<artifactId>java-jwt</artifactId>
<version>3.8.3</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-spring-boot-starter</artifactId>
</dependency>
</dependencies>
<build>
......
package eu.hbp.mip.configuration;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import org.keycloak.KeycloakSecurityContext;
public class KeycloakConfiguration {
@Autowired
private HttpServletRequest request;
public KeycloakSecurityContext getKeycloakSecurityContext() {
return (KeycloakSecurityContext) request.getAttribute(KeycloakSecurityContext.class.getName());
}
}
\ No newline at end of file
......@@ -8,6 +8,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
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.context.embedded.FilterRegistrationBean;
......@@ -34,6 +35,10 @@ import org.springframework.security.web.csrf.CsrfTokenRepository;
import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.WebUtils;
import org.springframework.security.oauth2.client.resource.BaseOAuth2ProtectedResourceDetails;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
......@@ -42,223 +47,217 @@ import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
// See https://spring.io/guides/tutorials/spring-boot-oauth2/ for reference about configuring OAuth2 login
// also http://cscarioni.blogspot.ch/2013/04/pro-spring-security-and-oauth-2.html
/**
* Configuration for security.
*/
@KeycloakConfiguration
public class SecurityConfiguration extends KeycloakWebSecurityConfigurerAdapter {
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(keycloakAuthenticationProvider());
}
@Configuration
@EnableOAuth2Client
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
/**
* Defines the session authentication strategy.
*/
@Bean
@Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new NullAuthenticatedSessionStrategy();
}
private static final Logger LOGGER = LoggerFactory.getLogger(SecurityConfiguration.class);
@Override
protected void configure(HttpSecurity http) throws Exception
{
super.configure(http);
http
.authorizeRequests()
.antMatchers("/user*").authenticated()
.antMatchers("/public1*").hasAuthority("public13")
.antMatchers("/public2*").hasAuthority("public2")
.antMatchers("/public3*").hasAuthority("public3")
.antMatchers("/admin*").authenticated()
.anyRequest().permitAll()
.and()
.logout()
.addLogoutHandler(keycloakLogoutHandler())//.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutUrl("/logout").permitAll()
.logoutSuccessUrl("/");
// .anyRequest().permitAll().and().logout().addLogoutHandler(new KeycloakLogoutHandler(new RestTemplate())).logoutRequestMatcher(new AntPathRequestMatcher("/logout"));
;
}
@Autowired
private OAuth2ClientContext oauth2ClientContext;
}
/**
* Enable HBP collab authentication (1) or disable it (0). Default is 1
*/
@Value("#{'${hbp.authentication.enabled:1}'}")
private boolean authentication;
/**
* Absolute URL to redirect to when login is required
*/
@Value("#{'${frontend.loginUrl:/login/hbp}'}")
private String loginUrl;
/**
* Absolute URL to redirect to after successful login
*/
@Value("#{'${frontend.redirectAfterLoginUrl:http://frontend/home}'}")
private String frontendRedirectAfterLogin;
/**
* Absolute URL to redirect to after logout has occurred
*/
@Value("#{'${frontend.redirectAfterLogoutUrl:/login/hbp}'}")
private String redirectAfterLogoutUrl;
/**
* URL to revoke auth token
*/
@Value("#{'${hbp.resource.revokeTokenUri:https://services.humanbrainproject.eu/oidc/revoke}'}")
private String revokeTokenURI;
//@Configuration
//@EnableOAuth2Client
//public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
//
// private static final Logger LOGGER = LoggerFactory.getLogger(SecurityConfiguration.class);
//
// @Autowired
// private OAuth2ClientContext oauth2ClientContext;
//
// /**
// * Enable HBP collab authentication (1) or disable it (0). Default is 1
// */
// @Value("#{'${hbp.authentication.enabled:1}'}")
// private boolean authentication;
//
// /**
// * Absolute URL to redirect to when login is required
// */
// @Value("#{'${frontend.loginUrl:/login/hbp}'}")
// private String loginUrl;
//
// /**
// * Absolute URL to redirect to after successful login
// */
// @Value("#{'${frontend.redirectAfterLoginUrl:http://frontend/home}'}")
// private String frontendRedirectAfterLogin;
//
// /**
// * Absolute URL to redirect to after logout has occurred
// */
// @Value("#{'${frontend.redirectAfterLogoutUrl:/login/hbp}'}")
// private String redirectAfterLogoutUrl;
//
// /**
// * URL to revoke auth token
// */
// @Value("#{'${hbp.resource.revokeTokenUri:https://services.humanbrainproject.eu/oidc/revoke}'}")
// private String revokeTokenURI;
//
//// @Autowired
//// private HttpServletRequest request;
//
// @Override
// protected void configure(HttpSecurity http) throws Exception {
// // @formatter:off
// http.addFilterBefore(new CORSFilter(), ChannelProcessingFilter.class);
//
// if (authentication) {
// private HttpServletRequest request;
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http.addFilterBefore(new CORSFilter(), ChannelProcessingFilter.class);
if (authentication) {
http.antMatcher("/**")
.authorizeRequests()
.antMatchers(
"/", "/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))
.and().logout().addLogoutHandler(new CustomLogoutHandler()).logoutSuccessUrl(redirectAfterLogoutUrl)
.and().logout().permitAll()
.and().csrf().ignoringAntMatchers("/logout").csrfTokenRepository(csrfTokenRepository())
.and().addFilterAfter(csrfHeaderFilter(), CsrfFilter.class)
.addFilterBefore(ssoFilter(), BasicAuthenticationFilter.class);
}
else {
//keycloak
//KeycloakConfiguration.getKeycloakSecurityContext();
// http.antMatcher("/**")
// .authorizeRequests()
// .antMatchers(
// "/", "/login/**", "/health/**", "/info/**", "/metrics/**", "/trace/**", "/frontend/**", "/webjars/**", "/v2/api-docs", "/swagger-ui.html", "/swagger-resources/**"
// ).permitAll()
// .anyRequest().authenticated()
// .and().exceptionHandling().authenticationEntryPoint(new CustomLoginUrlAuthenticationEntryPoint(loginUrl))
// .and().logout().addLogoutHandler(new CustomLogoutHandler()).logoutSuccessUrl(redirectAfterLogoutUrl)
// .and().logout().permitAll()
// .and().csrf().ignoringAntMatchers("/logout").csrfTokenRepository(csrfTokenRepository())
// .and().addFilterAfter(csrfHeaderFilter(), CsrfFilter.class)
// .addFilterBefore(ssoFilter(), BasicAuthenticationFilter.class);
// }
// else {
// //keycloak
// //KeycloakConfiguration.getKeycloakSecurityContext();
//// http.antMatcher("/**")
//// .authorizeRequests()
//// .antMatchers("/**").permitAll().and().csrf().disable();
// }
// }
//
// private Filter ssoFilter() {
// OAuth2ClientAuthenticationProcessingFilter hbpFilter = new OAuth2ClientAuthenticationProcessingFilter("/login/hbp");
// OAuth2RestTemplate hbpTemplate = new OAuth2RestTemplate(hbp(), oauth2ClientContext);
// hbpFilter.setAuthenticationSuccessHandler(new SimpleUrlAuthenticationSuccessHandler(frontendRedirectAfterLogin));
// hbpFilter.setRestTemplate(hbpTemplate);
// hbpFilter.setTokenServices(new UserInfoTokenServices(hbpResource().getUserInfoUri(), hbp().getClientId()));
// return hbpFilter;
// }
//
// @Bean
// public FilterRegistrationBean oauth2ClientFilterRegistration(
// OAuth2ClientContextFilter filter) {
// FilterRegistrationBean registration = new FilterRegistrationBean();
// registration.setFilter(filter);
// registration.setOrder(-100);
// return registration;
// }
//
// @Bean(name="hbp")
// @ConfigurationProperties("hbp.client")
// public OAuth2ProtectedResourceDetails hbp() {
// return new AuthorizationCodeResourceDetails();
// }
//
// @Bean(name="hbpResource")
// @ConfigurationProperties("hbp.resource")
// public ResourceServerProperties hbpResource() {
// return new ResourceServerProperties();
// }
//
// public boolean isAuthentication() {
// return authentication;
// }
//
// public String getFrontendRedirectAfterLogin() {
// return frontendRedirectAfterLogin;
// }
//
// private Filter csrfHeaderFilter() {
// return new OncePerRequestFilter() {
// @Override
// protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
// FilterChain filterChain) throws ServletException, IOException {
// CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
// if (csrf != null) {
// Cookie cookie = WebUtils.getCookie(request, "XSRF-TOKEN");
// String token = csrf.getToken();
// if (cookie == null || token != null && !token.equals(cookie.getValue())) {
// cookie = new Cookie("XSRF-TOKEN", token);
// cookie.setPath("/");
// response.addCookie(cookie);
// }
// }
// filterChain.doFilter(request, response);
// }
// };
// }
//
// private CsrfTokenRepository csrfTokenRepository() {
// HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
// repository.setHeaderName("X-XSRF-TOKEN");
// 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);
// }
//
// }
// }
//}
// .antMatchers("/**").permitAll().and().csrf().disable();
}
}
private Filter ssoFilter() {
OAuth2ClientAuthenticationProcessingFilter hbpFilter = new OAuth2ClientAuthenticationProcessingFilter("/login/hbp");
OAuth2RestTemplate hbpTemplate = new OAuth2RestTemplate(hbp(), oauth2ClientContext);
hbpFilter.setAuthenticationSuccessHandler(new SimpleUrlAuthenticationSuccessHandler(frontendRedirectAfterLogin));
hbpFilter.setRestTemplate(hbpTemplate);
hbpFilter.setTokenServices(new UserInfoTokenServices(hbpResource().getUserInfoUri(), hbp().getClientId()));
return hbpFilter;
}
@Bean
public FilterRegistrationBean oauth2ClientFilterRegistration(
OAuth2ClientContextFilter filter) {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(filter);
registration.setOrder(-100);
return registration;
}
@Bean(name="hbp")
@ConfigurationProperties("hbp.client")
public BaseOAuth2ProtectedResourceDetails hbp() {
return new AuthorizationCodeResourceDetails();
}
@Bean(name="hbpResource")
@ConfigurationProperties("hbp.resource")
public ResourceServerProperties hbpResource() {
return new ResourceServerProperties();
}
public boolean isAuthentication() {
return authentication;
}
public String getFrontendRedirectAfterLogin() {
return frontendRedirectAfterLogin;
}
private Filter csrfHeaderFilter() {
return new OncePerRequestFilter() {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
if (csrf != null) {
Cookie cookie = WebUtils.getCookie(request, "XSRF-TOKEN");
String token = csrf.getToken();
if (cookie == null || token != null && !token.equals(cookie.getValue())) {
cookie = new Cookie("XSRF-TOKEN", token);
cookie.setPath("/");
response.addCookie(cookie);
}
}
filterChain.doFilter(request, response);
}
};
}
private CsrfTokenRepository csrfTokenRepository() {
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
repository.setHeaderName("X-XSRF-TOKEN");
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();
}
public class KeycloakAuthoritiesExtractor
implements AuthoritiesExtractor {
@Override
public List<GrantedAuthority> extractAuthorities
(Map<String, Object> map) {
return AuthorityUtils
.commaSeparatedStringToAuthorityList(asAuthorities(map));
}
private String asAuthorities(Map<String, Object> map) {
List<String> authorities = new ArrayList<>();
// authorities.add("BAELDUNG_USER");
List<LinkedHashMap<String, String>> authz;
authz = (List<LinkedHashMap<String, String>>) map.get("authorities");
for (LinkedHashMap<String, String> entry : authz) {
authorities.add(entry.get("authority"));
}
return String.join(",", authorities);
}
}
}
......@@ -17,6 +17,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.security.access.prepost.PreAuthorize;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
......@@ -102,6 +103,7 @@ public class SecurityApi {
*/
@RequestMapping(path = "/galaxy", method = RequestMethod.GET, produces = "application/json")
@PreAuthorize("hasRole('Data Manager')")
@ResponseStatus(value = HttpStatus.OK)
public ResponseEntity getGalaxyConfiguration(){
String stringEncoded = Base64.getEncoder().encodeToString((galaxyUsername + ":" + galaxyPassword).getBytes());
......
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