Added OAuth2 AuthServer and two ResServer for admin and monitoring

SEBSERV-4
This commit is contained in:
anhefti 2018-11-22 10:39:18 +01:00
parent 807cae3e51
commit cfa4525c21
14 changed files with 681 additions and 96 deletions

View file

@ -16,6 +16,7 @@ import org.springframework.context.annotation.Configuration;
@Configuration
@SpringBootApplication(exclude = {
// OAuth2ResourceServerAutoConfiguration.class,
UserDetailsServiceAutoConfiguration.class,
DataSourceAutoConfiguration.class
})

View file

@ -10,10 +10,6 @@ package ch.ethz.seb.sebserver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@ -25,21 +21,13 @@ import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
@Configuration
@WebServiceProfile
@GuiProfile
@EnableWebSecurity
@Order(0)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
public class WebSecurityConfig {
/** Spring bean name of user password encoder */
public static final String USER_PASSWORD_ENCODER_BEAN_NAME = "userPasswordEncoder";
/** Spring bean name of client (application) password encoder */
public static final String CLIENT_PASSWORD_ENCODER_BEAN_NAME = "clientPasswordEncoder";
@Override
protected void configure(final HttpSecurity http) throws Exception {
System.out.println("**************** Overall WebConfig: ");
}
/** Password encoder used for user passwords (stronger protection) */
@Bean(USER_PASSWORD_ENCODER_BEAN_NAME)
public PasswordEncoder userPasswordEncoder() {

View file

@ -0,0 +1,34 @@
/*
* Copyright (c) 2018 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.gbl;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.joda.JodaModule;
@Lazy
@Component
public class JSONMapper extends ObjectMapper {
private static final long serialVersionUID = 2883304481547670626L;
public JSONMapper() {
super();
super.registerModule(new JodaModule());
super.configure(
com.fasterxml.jackson.databind.SerializationFeature.WRITE_DATES_AS_TIMESTAMPS,
false);
super.configure(
com.fasterxml.jackson.databind.SerializationFeature.WRITE_DATES_WITH_ZONE_ID,
false);
}
}

View file

@ -9,97 +9,88 @@
package ch.ethz.seb.sebserver.webservice.weblayer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfiguration;
import org.springframework.security.oauth2.provider.token.AccessTokenConverter;
import org.springframework.security.oauth2.provider.token.DefaultAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.DefaultUserAuthenticationConverter;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.UserAuthenticationConverter;
import ch.ethz.seb.sebserver.WebSecurityConfig;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
import ch.ethz.seb.sebserver.webservice.weblayer.oauth.AdminResourceServerConfig;
import ch.ethz.seb.sebserver.webservice.weblayer.oauth.SebClientResourceServerConfig;
import ch.ethz.seb.sebserver.webservice.weblayer.oauth.WebServiceClientDetails;
/** Spring web security configuration for all endpoints needed for SEB-Client session management.
* This are:
*
* <pre>
* - /sebauth/sebhandshake/ the SEB-Client handshake and authentication endpoint
* - /sebauth/lmshandshake/ the LMS-Client handshake and authentication endpoint
* - /ws/ the root of all web-socket endpoints on HTTP level
* </pre>
*
* This configuration secures the above endpoints by using custom client authentication filter */
@Configuration
@WebServiceProfile
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
@Order(2)
@EnableWebSecurity
public class ClientSessionWebSecurityConfig extends WebSecurityConfigurerAdapter {
public static final AntPathRequestMatcher SEB_HANDSHAKE_ENDPOINT =
new AntPathRequestMatcher("/sebauth/sebhandshake/**");
public static final AntPathRequestMatcher SEB_WEB_SOCKET_ENDPOINT =
new AntPathRequestMatcher("/ws/**");
public static final AntPathRequestMatcher LMS_HANDSHAKE_ENDPOINT =
new AntPathRequestMatcher("/sebauth/lmshandshake/**");
public static final RequestMatcher SEB_CLIENT_ENDPOINTS = new OrRequestMatcher(
SEB_HANDSHAKE_ENDPOINT,
SEB_WEB_SOCKET_ENDPOINT);
public static final RequestMatcher SEB_CONNECTION_PROTECTED_URLS = new OrRequestMatcher(
SEB_CLIENT_ENDPOINTS,
LMS_HANDSHAKE_ENDPOINT);
/** Spring bean name of user password encoder */
public static final String AUTHENTICATION_MANAGER = "AUTHENTICATION_MANAGER";
@Autowired
private CustomAuthenticationError customAuthenticationError;
private WebServiceUserDetails webServiceUserDetails;
@Autowired
@Qualifier(WebSecurityConfig.USER_PASSWORD_ENCODER_BEAN_NAME)
private PasswordEncoder userPasswordEncoder;
@Autowired
private TokenStore tokenStore;
@Autowired
private WebServiceClientDetails webServiceClientDetails;
@Bean
public AccessTokenConverter accessTokenConverter() {
final DefaultAccessTokenConverter accessTokenConverter = new DefaultAccessTokenConverter();
accessTokenConverter.setUserTokenConverter(userAuthenticationConverter());
return accessTokenConverter;
}
@Bean
public UserAuthenticationConverter userAuthenticationConverter() {
final DefaultUserAuthenticationConverter userAuthenticationConverter =
new DefaultUserAuthenticationConverter();
userAuthenticationConverter.setUserDetailsService(this.webServiceUserDetails);
return userAuthenticationConverter;
}
@Override
protected void configure(final HttpSecurity http) throws Exception {
System.out.println("**************** WebServiceWebConfig: ");
//@formatter:off
http
// The Web-Service is designed as a stateless Rest API
// for SEB session management only endpoints for handshake and web-socket is used what is stateless on HTTP
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
// TODO
// .and()
// .requestMatcher(SEB_CONNECTION_PROTECTED_URLS)
// .addFilterBefore(
// this.sebClientAuthenticationFilter,
// BasicAuthenticationFilter.class)
// .addFilterBefore(
// this.lmsClientAuthenticationFilter,
// SEBClientAuthenticationFilter.class)
// .authorizeRequests()
// .requestMatchers(SEB_CONNECTION_PROTECTED_URLS)
// .authenticated()
// instead of:
.and()
.antMatcher("/webservice/**")
.authorizeRequests()
.anyRequest()
.fullyAuthenticated()
// end TODO
.and()
.exceptionHandling()
.defaultAuthenticationEntryPointFor(
this.customAuthenticationError,
SEB_CONNECTION_PROTECTED_URLS)
.and()
// disable session based security and functionality
.formLogin().disable()
.httpBasic().disable()
.logout().disable()
.headers().frameOptions().disable()
.and()
.csrf().disable();
//@formatter:on
@Bean(AUTHENTICATION_MANAGER)
public AuthenticationManager authenticationManagerBean() throws Exception {
final AuthenticationManager authenticationManagerBean = super.authenticationManagerBean();
return authenticationManagerBean;
}
@Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(this.webServiceUserDetails)
.passwordEncoder(this.userPasswordEncoder);
}
@Bean
protected ResourceServerConfiguration sebServerAdminAPIResources() {
return new AdminResourceServerConfig(accessTokenConverter());
}
@Bean
protected ResourceServerConfiguration sebServerSebClientAPIResources() throws Exception {
return new SebClientResourceServerConfig(
accessTokenConverter(),
this.tokenStore,
this.webServiceClientDetails,
authenticationManagerBean());
}
}

View file

@ -0,0 +1,29 @@
/*
* Copyright (c) 2018 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.webservice.weblayer;
import java.security.Principal;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
@RestController
@RequestMapping("/sebclient")
@WebServiceProfile
public class SebClientTestController {
@RequestMapping(value = "/hello", method = RequestMethod.GET)
public String helloFromWebService(final Principal principal) {
return "Hello From Seb-Cleint-Web-Service";
}
}

View file

@ -0,0 +1,48 @@
/*
* Copyright (c) 2018 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.webservice.weblayer;
import java.util.Collections;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
@Lazy
@Component
public class WebServiceUserDetails implements UserDetailsService {
// private final UserDao userDao;
//
// public InternalUserDetailsService(final UserDao userDao) {
// this.userDao = userDao;
// }
@Override
public UserDetails loadUserByUsername(final String username) throws UsernameNotFoundException {
return new User(
username,
"$2a$04$btj5PkII8IIHLE7zbQOd3u7YghHeClG7k1ZzYbtybRnd5h1YqwTf.",
Collections.emptyList());
// try {
// final org.eth.demo.sebserver.domain.rest.admin.User byUserName = this.userDao.byUserName(username);
// if (byUserName == null) {
// throw new UsernameNotFoundException("No User with name: " + username + " found");
// }
// return byUserName;
// } catch (final Exception e) {
// throw new UsernameNotFoundException("No User with name: " + username + " found");
// }
}
}

View file

@ -8,6 +8,8 @@
package ch.ethz.seb.sebserver.webservice.weblayer;
import java.security.Principal;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@ -15,7 +17,7 @@ import org.springframework.web.bind.annotation.RestController;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
@RestController
@RequestMapping("/webservice")
@RequestMapping("/admin")
@WebServiceProfile
public class WsTestController {
@ -23,9 +25,9 @@ public class WsTestController {
System.out.println("************** TestController webservice");
}
@RequestMapping(value = "/", method = RequestMethod.GET)
public String helloFromWebService() {
return "Hello From Web-Service";
@RequestMapping(value = "/hello", method = RequestMethod.GET)
public String helloFromWebService(final Principal principal) {
return "Hello From Admin-Web-Service";
}
}

View file

@ -0,0 +1,89 @@
/*
* Copyright (c) 2018 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.webservice.weblayer.oauth;
import java.util.Arrays;
import java.util.List;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfiguration;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.AccessTokenConverter;
import org.springframework.security.oauth2.provider.token.RemoteTokenServices;
import org.springframework.web.util.UriComponentsBuilder;
public class AdminResourceServerConfig extends ResourceServerConfiguration {
@Value("${server.address}")
private String webServerAdress;
@Value("${server.port}")
private String webServerPort;
@Value("${sebserver.webservice.protocol}")
private String webProtocol;
@Value("${sebserver.oauth.clients.guiClient.id}")
private String guiClientId;
// TODO secret should not be referenced here (should go to stack and disappear after use)
@Value("${sebserver.oauth.clients.guiClient.secret}")
private String guiClientSecret;
public AdminResourceServerConfig(final AccessTokenConverter accessTokenConverter) {
setConfigurers(Arrays.<ResourceServerConfigurer> asList(new ResourceServerConfigurerAdapter() {
@Override
public void configure(final ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId(GuiClientDetails.RESOURCE_ID);
// TODO try to use DefualtTokenServices like in SebClientResourceServerConfig
final RemoteTokenServices tokenService = new RemoteTokenServices();
tokenService.setCheckTokenEndpointUrl(
UriComponentsBuilder
.fromHttpUrl(AdminResourceServerConfig.this.webProtocol + "://"
+ AdminResourceServerConfig.this.webServerAdress)
.port(AdminResourceServerConfig.this.webServerPort)
.path("oauth/check_token")
.toUriString());
tokenService.setClientId(AdminResourceServerConfig.this.guiClientId);
tokenService.setClientSecret(AdminResourceServerConfig.this.guiClientSecret);
tokenService.setAccessTokenConverter(accessTokenConverter);
resources.tokenServices(tokenService);
}
@Override
public void configure(final HttpSecurity http) throws Exception {
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.antMatcher("/admin/**")
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.formLogin().disable()
.httpBasic().disable()
.logout().disable()
.headers().frameOptions().disable()
.and()
.csrf().disable();
}
}));
setOrder(1);
}
// Switch off the Spring Boot auto configuration
@Override
public void setConfigurers(final List<ResourceServerConfigurer> configurers) {
super.setConfigurers(configurers);
}
}

View file

@ -0,0 +1,85 @@
/*
* Copyright (c) 2018 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.webservice.weblayer.oauth;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.AccessTokenConverter;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import ch.ethz.seb.sebserver.WebSecurityConfig;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
import ch.ethz.seb.sebserver.webservice.weblayer.ClientSessionWebSecurityConfig;
import ch.ethz.seb.sebserver.webservice.weblayer.WebServiceUserDetails;
@WebServiceProfile
@Configuration
@EnableAuthorizationServer
@Order(100)
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private AccessTokenConverter accessTokenConverter;
@Autowired
private DataSource dataSource;
@Autowired
private WebServiceUserDetails webServiceUserDetails;
@Autowired
private WebServiceClientDetails webServiceClientDetails;
@Autowired
@Qualifier(WebSecurityConfig.CLIENT_PASSWORD_ENCODER_BEAN_NAME)
private PasswordEncoder clientPasswordEncoder;
@Autowired
@Qualifier(ClientSessionWebSecurityConfig.AUTHENTICATION_MANAGER)
private AuthenticationManager authenticationManager;
@Override
public void configure(final AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer
.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()")
.passwordEncoder(this.clientPasswordEncoder);
}
@Override
public void configure(final ClientDetailsServiceConfigurer clients) throws Exception {
clients.withClientDetails(this.webServiceClientDetails);
}
@Bean
public TokenStore tokenStore() {
return new JdbcTokenStore(this.dataSource);
}
@Override
public void configure(final AuthorizationServerEndpointsConfigurer endpoints) {
final JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
jwtAccessTokenConverter.setAccessTokenConverter(this.accessTokenConverter);
endpoints
.tokenStore(tokenStore())
.authenticationManager(this.authenticationManager)
.userDetailsService(this.webServiceUserDetails)
.accessTokenConverter(jwtAccessTokenConverter);
}
}

View file

@ -0,0 +1,160 @@
/*
* Copyright (c) 2018 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.webservice.weblayer.oauth;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.stereotype.Component;
import ch.ethz.seb.sebserver.WebSecurityConfig;
import ch.ethz.seb.sebserver.gbl.util.Utils;
@Lazy
@Component
public final class GuiClientDetails implements ClientDetails {
private static final long serialVersionUID = 4505193832353978832L;
public static final String RESOURCE_ID = "seb-server-administration-api";
private static final Set<String> GRANT_TYPES = Utils.immutableSetOf("password", "refresh_token");
private static final Set<String> RESOURCE_IDS = Utils.immutableSetOf(RESOURCE_ID);
private static final Set<String> SCOPES = Utils.immutableSetOf("read", "write");
private final String guiClientId;
private final String guiClientSecret;
private final Integer guiClientAccessTokenValiditySeconds;
private final Integer guiClientRefreshTokenValiditySeconds;
public GuiClientDetails(
@Qualifier(WebSecurityConfig.CLIENT_PASSWORD_ENCODER_BEAN_NAME) final PasswordEncoder clientPasswordEncoder,
@Value("${sebserver.oauth.clients.guiClient.id}") final String guiClientId,
@Value("${sebserver.oauth.clients.guiClient.secret}") final String guiClientSecret,
@Value("${sebserver.oauth.clients.guiClient.accessTokenValiditySeconds}") final Integer guiClientAccessTokenValiditySeconds,
@Value("${sebserver.oauth.clients.guiClient.refreshTokenValiditySeconds}") final Integer guiClientRefreshTokenValiditySeconds) {
this.guiClientId = guiClientId;
this.guiClientSecret = clientPasswordEncoder.encode(guiClientSecret);
this.guiClientAccessTokenValiditySeconds = guiClientAccessTokenValiditySeconds;
this.guiClientRefreshTokenValiditySeconds = guiClientRefreshTokenValiditySeconds;
}
@Autowired
@Qualifier(WebSecurityConfig.CLIENT_PASSWORD_ENCODER_BEAN_NAME)
private PasswordEncoder clientPasswordEncoder;
@Override
public String getClientId() {
return this.guiClientId;
}
@Override
public Set<String> getResourceIds() {
return RESOURCE_IDS;
}
@Override
public boolean isSecretRequired() {
return true;
}
@Override
public String getClientSecret() {
return this.guiClientSecret;
}
@Override
public boolean isScoped() {
return true;
}
@Override
public Set<String> getScope() {
return SCOPES;
}
@Override
public Set<String> getAuthorizedGrantTypes() {
return GRANT_TYPES;
}
@Override
public Set<String> getRegisteredRedirectUri() {
return Collections.emptySet();
}
@Override
public Collection<GrantedAuthority> getAuthorities() {
return Collections.emptyList();
}
@Override
public Integer getAccessTokenValiditySeconds() {
return this.guiClientAccessTokenValiditySeconds;
}
@Override
public Integer getRefreshTokenValiditySeconds() {
return this.guiClientRefreshTokenValiditySeconds;
}
@Override
public boolean isAutoApprove(final String scope) {
return true;
}
@Override
public Map<String, Object> getAdditionalInformation() {
return Collections.emptyMap();
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((this.guiClientId == null) ? 0 : this.guiClientId.hashCode());
return result;
}
@Override
public boolean equals(final Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final GuiClientDetails other = (GuiClientDetails) obj;
if (this.guiClientId == null) {
if (other.guiClientId != null)
return false;
} else if (!this.guiClientId.equals(other.guiClientId))
return false;
return true;
}
@Override
public String toString() {
return "GuiClientDetails [guiClientId=" + this.guiClientId + ", guiClientSecret=" + this.guiClientSecret
+ ", guiClientAccessTokenValiditySeconds=" + this.guiClientAccessTokenValiditySeconds
+ ", guiClientRefreshTokenValiditySeconds=" + this.guiClientRefreshTokenValiditySeconds
+ ", clientPasswordEncoder=" + this.clientPasswordEncoder + "]";
}
}

View file

@ -0,0 +1,75 @@
/*
* Copyright (c) 2018 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.webservice.weblayer.oauth;
import java.util.Arrays;
import java.util.List;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfiguration;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.AccessTokenConverter;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
public class SebClientResourceServerConfig extends ResourceServerConfiguration {
public SebClientResourceServerConfig(
final AccessTokenConverter accessTokenConverter,
final TokenStore tokenStore,
final WebServiceClientDetails webServiceClientDetails,
final AuthenticationManager authenticationManager) {
setConfigurers(Arrays.<ResourceServerConfigurer> asList(new ResourceServerConfigurerAdapter() {
@Override
public void configure(final ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId(WebServiceClientDetails.RESOURCE_ID);
final DefaultTokenServices tokenService = new DefaultTokenServices();
tokenService.setTokenStore(tokenStore);
tokenService.setClientDetailsService(webServiceClientDetails);
tokenService.setSupportRefreshToken(false);
tokenService.setAuthenticationManager(authenticationManager);
resources.tokenServices(tokenService);
}
@Override
public void configure(final HttpSecurity http) throws Exception {
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.antMatcher("/sebclient/**")
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.formLogin().disable()
.httpBasic().disable()
.logout().disable()
.headers().frameOptions().disable()
.and()
.csrf().disable();
}
}));
setOrder(2);
}
// Switch off the Spring Boot auto configuration
@Override
public void setConfigurers(final List<ResourceServerConfigurer> configurers) {
super.setConfigurers(configurers);
}
}

View file

@ -0,0 +1,80 @@
/*
* Copyright (c) 2018 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.webservice.weblayer.oauth;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.ClientRegistrationException;
import org.springframework.security.oauth2.provider.client.BaseClientDetails;
import org.springframework.stereotype.Component;
import ch.ethz.seb.sebserver.WebSecurityConfig;
@Lazy
@Component
public class WebServiceClientDetails implements ClientDetailsService {
private static final Logger log = LoggerFactory.getLogger(WebServiceClientDetails.class);
public static final String[] SEB_CLIENT_GRANT_TYPES = new String[] { "client_credentials", "refresh_token" };
public static final String[] SEB_CLIENT_SCOPES = new String[] { "web-service-api-read", "web-service-api-write" };
public static final String RESOURCE_ID = "seb-server-seb-client-api";
@Value("${sebserver.oauth.clients.guiClient.accessTokenValiditySeconds}")
private Integer guiClientAccessTokenValiditySeconds;
@Value("${sebserver.oauth.clients.guiClient.refreshTokenValiditySeconds}")
private Integer guiClientRefreshTokenValiditySeconds;
private final GuiClientDetails guiClientDetails;
@Autowired
@Qualifier(WebSecurityConfig.CLIENT_PASSWORD_ENCODER_BEAN_NAME)
private PasswordEncoder clientPasswordEncoder;
public WebServiceClientDetails(final GuiClientDetails guiClientDetails) {
this.guiClientDetails = guiClientDetails;
}
@Override
public ClientDetails loadClientByClientId(final String clientId) throws ClientRegistrationException {
if (clientId == null) {
throw new ClientRegistrationException("clientId is null");
}
if (clientId.equals(this.guiClientDetails.getClientId())) {
return this.guiClientDetails;
}
final ClientDetails forSEBClientAPI = getForSEBClientAPI(clientId);
if (forSEBClientAPI != null) {
return forSEBClientAPI;
}
log.warn("ClientDetails for clientId: {} not found", clientId);
throw new ClientRegistrationException("clientId not found");
}
private ClientDetails getForSEBClientAPI(final String clientId) {
// TODO create ClientDetails from matching LMSSetup
final BaseClientDetails baseClientDetails = new BaseClientDetails(
clientId,
RESOURCE_ID,
"read,write",
"client_credentials,refresh_token", "");
baseClientDetails.setClientSecret(this.clientPasswordEncoder.encode("test"));
return baseClientDetails;
}
}

View file

@ -1,6 +1,6 @@
server.address=localhost
server.port=8090
server.servlet.context-path=/api/
server.servlet.context-path=/
spring.datasource.initialize=true
spring.datasource.initialization-mode=always
@ -10,6 +10,8 @@ spring.datasource.platform=dev
sebserver.oauth.clients.guiClient.accessTokenValiditySeconds=1800
sebserver.oauth.clients.guiClient.refreshTokenValiditySeconds=-1
sebserver.oauth.clients.sebClient.accessTokenValiditySeconds=1800
sebserver.oauth.clients.sebClient.refreshTokenValiditySeconds=-1
sebserver.webservice.protocol=http
sebserver.client.connection-strategy=HTTP

View file

@ -13,6 +13,7 @@
<Logger name="org.springframework.messaging" level="INFO" additivity="true" />
<Logger name="org.springframework.web" level="DEBUG" additivity="true" />
<Logger name="org.springframework.security.oauth2" level="DEBUG" additivity="true" />
<root level="INFO" additivity="true">
<appender-ref ref="STDOUT" />