diff --git a/src/main/java/ch/ethz/seb/sebserver/SEBServer.java b/src/main/java/ch/ethz/seb/sebserver/SEBServer.java
index 0c23f5b2..225c13d9 100644
--- a/src/main/java/ch/ethz/seb/sebserver/SEBServer.java
+++ b/src/main/java/ch/ethz/seb/sebserver/SEBServer.java
@@ -16,6 +16,7 @@ import org.springframework.context.annotation.Configuration;
@Configuration
@SpringBootApplication(exclude = {
+ // OAuth2ResourceServerAutoConfiguration.class,
UserDetailsServiceAutoConfiguration.class,
DataSourceAutoConfiguration.class
})
diff --git a/src/main/java/ch/ethz/seb/sebserver/WebSecurityConfig.java b/src/main/java/ch/ethz/seb/sebserver/WebSecurityConfig.java
index 9f7e8e50..d85e7114 100644
--- a/src/main/java/ch/ethz/seb/sebserver/WebSecurityConfig.java
+++ b/src/main/java/ch/ethz/seb/sebserver/WebSecurityConfig.java
@@ -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() {
diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/JSONMapper.java b/src/main/java/ch/ethz/seb/sebserver/gbl/JSONMapper.java
new file mode 100644
index 00000000..959d9ffc
--- /dev/null
+++ b/src/main/java/ch/ethz/seb/sebserver/gbl/JSONMapper.java
@@ -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);
+ }
+
+}
diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/ClientSessionWebSecurityConfig.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/ClientSessionWebSecurityConfig.java
index 6429bc29..2f37d421 100644
--- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/ClientSessionWebSecurityConfig.java
+++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/ClientSessionWebSecurityConfig.java
@@ -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:
- *
- *
- * - /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
- *
- *
- * 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());
+ }
+
}
diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/SebClientTestController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/SebClientTestController.java
new file mode 100644
index 00000000..6225f8f1
--- /dev/null
+++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/SebClientTestController.java
@@ -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";
+ }
+
+}
diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/WebServiceUserDetails.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/WebServiceUserDetails.java
new file mode 100644
index 00000000..0922d9c0
--- /dev/null
+++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/WebServiceUserDetails.java
@@ -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");
+// }
+ }
+
+}
diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/WsTestController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/WsTestController.java
index 6743fc27..6517c54e 100644
--- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/WsTestController.java
+++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/WsTestController.java
@@ -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";
}
}
diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/oauth/AdminResourceServerConfig.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/oauth/AdminResourceServerConfig.java
new file mode 100644
index 00000000..5656dfd7
--- /dev/null
+++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/oauth/AdminResourceServerConfig.java
@@ -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. 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 configurers) {
+ super.setConfigurers(configurers);
+ }
+
+}
diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/oauth/AuthorizationServerConfig.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/oauth/AuthorizationServerConfig.java
new file mode 100644
index 00000000..e9765cc6
--- /dev/null
+++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/oauth/AuthorizationServerConfig.java
@@ -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);
+ }
+
+}
diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/oauth/GuiClientDetails.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/oauth/GuiClientDetails.java
new file mode 100644
index 00000000..5056ebce
--- /dev/null
+++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/oauth/GuiClientDetails.java
@@ -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 GRANT_TYPES = Utils.immutableSetOf("password", "refresh_token");
+ private static final Set RESOURCE_IDS = Utils.immutableSetOf(RESOURCE_ID);
+ private static final Set 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 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 getScope() {
+ return SCOPES;
+ }
+
+ @Override
+ public Set getAuthorizedGrantTypes() {
+ return GRANT_TYPES;
+ }
+
+ @Override
+ public Set getRegisteredRedirectUri() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public Collection 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 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 + "]";
+ }
+
+}
diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/oauth/SebClientResourceServerConfig.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/oauth/SebClientResourceServerConfig.java
new file mode 100644
index 00000000..d808358d
--- /dev/null
+++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/oauth/SebClientResourceServerConfig.java
@@ -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. 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 configurers) {
+ super.setConfigurers(configurers);
+ }
+
+}
diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/oauth/WebServiceClientDetails.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/oauth/WebServiceClientDetails.java
new file mode 100644
index 00000000..c4b91569
--- /dev/null
+++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/oauth/WebServiceClientDetails.java
@@ -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;
+ }
+
+}
diff --git a/src/main/resources/config/application-dev-ws.properties b/src/main/resources/config/application-dev-ws.properties
index de055c2c..c260d96e 100644
--- a/src/main/resources/config/application-dev-ws.properties
+++ b/src/main/resources/config/application-dev-ws.properties
@@ -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
diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml
index 1be5cd9a..af31e7ad 100644
--- a/src/main/resources/logback.xml
+++ b/src/main/resources/logback.xml
@@ -13,6 +13,7 @@
+