diff --git a/common.iml b/common.iml
new file mode 100644
index 0000000..66b3d79
--- /dev/null
+++ b/common.iml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/oauth-provider-api/oauth-provider-api.iml b/oauth-provider-api/oauth-provider-api.iml
index 1009c43..b82857a 100644
--- a/oauth-provider-api/oauth-provider-api.iml
+++ b/oauth-provider-api/oauth-provider-api.iml
@@ -5,20 +5,17 @@
-
-
+
-
-
-
-
-
-
+
+
+
+
diff --git a/oauth-provider-api/pom.xml b/oauth-provider-api/pom.xml
index 66ea3c0..aaa56ec 100644
--- a/oauth-provider-api/pom.xml
+++ b/oauth-provider-api/pom.xml
@@ -23,6 +23,11 @@
feign-core
${feign.version}
+
+ org.hibernate
+ hibernate-validator
+ ${hibernate-validator.version}
+
com.fasterxml.jackson.core
jackson-databind
diff --git a/oauth-provider-api/src/main/java/de/helfenkannjeder/oauth/provider/api/dto/UserRequestDto.java b/oauth-provider-api/src/main/java/de/helfenkannjeder/oauth/provider/api/dto/UserRequestDto.java
index 557459c..0855c06 100644
--- a/oauth-provider-api/src/main/java/de/helfenkannjeder/oauth/provider/api/dto/UserRequestDto.java
+++ b/oauth-provider-api/src/main/java/de/helfenkannjeder/oauth/provider/api/dto/UserRequestDto.java
@@ -1,11 +1,20 @@
package de.helfenkannjeder.oauth.provider.api.dto;
+import org.hibernate.validator.constraints.NotEmpty;
+
+import javax.validation.constraints.NotNull;
+
/**
* @author Valentin Zickner
*/
public class UserRequestDto {
+ @NotNull
+ @NotEmpty
private String username;
+
+ @NotNull
+ @NotEmpty
private String password;
public UserRequestDto() {
diff --git a/oauth-provider/oauth-provider.iml b/oauth-provider/oauth-provider.iml
index 308bafe..c7b5969 100644
--- a/oauth-provider/oauth-provider.iml
+++ b/oauth-provider/oauth-provider.iml
@@ -1,14 +1,28 @@
-
+
+
+
+
+ spring_boot_de.helfenkannjeder.oauth.provider.configuration.OAuth2ProviderApplication
+ file://$MODULE_DIR$/src/main/java/de/helfenkannjeder/oauth/provider/configuration/OAuth2ProviderApplication.java
+ file://$MODULE_DIR$/src/main/java/de/helfenkannjeder/oauth/provider/configuration/OAuth2ProviderConfiguration.java
+ file://$MODULE_DIR$/src/main/java/de/helfenkannjeder/oauth/provider/configuration/SecurityConfiguration.java
+
+
+
+
+
+
+
-
+
@@ -17,11 +31,8 @@
-
-
-
@@ -29,19 +40,69 @@
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/oauth-provider/pom.xml b/oauth-provider/pom.xml
index 85a97d7..4744a12 100644
--- a/oauth-provider/pom.xml
+++ b/oauth-provider/pom.xml
@@ -6,6 +6,13 @@
de.helfenkannjeder.common
oauth-provider
+
+ 1.8
+ 0.0.1-SNAPSHOT
+ 4.12
+ 1.4.182
+
+
org.springframework.boot
spring-boot-starter-parent
@@ -19,5 +26,54 @@
org.springframework.boot
spring-boot-starter-web
+
+ org.springframework.security.oauth
+ spring-security-oauth2
+
+
+ de.helfenkannjeder.common
+ oauth-provider-api
+ ${common.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+ mysql
+ mysql-connector-java
+ runtime
+
+
+ org.hibernate
+ hibernate-validator
+
+
+ com.h2database
+ h2
+ ${h2.version}
+ test
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ org.springframework.security
+ spring-security-test
+ test
+
+
+ junit
+ junit
+ ${junit.version}
+ test
+
+
+ com.jayway.jsonpath
+ json-path
+ test
+
diff --git a/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/SampleController.java b/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/SampleController.java
deleted file mode 100644
index 4dc88ce..0000000
--- a/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/SampleController.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package de.helfenkannjeder.oauth.provider;
-
-import org.springframework.boot.SpringApplication;
-import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
-import org.springframework.stereotype.Controller;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.ResponseBody;
-
-/**
- * @author Valentin Zickner
- */
-@Controller
-@EnableAutoConfiguration
-public class SampleController {
-
- @RequestMapping("/")
- @ResponseBody
- String home() {
- return "Hello World!";
- }
-
- public static void main(String[] args) throws Exception {
- SpringApplication.run(SampleController.class, args);
- }
-}
\ No newline at end of file
diff --git a/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/configuration/OAuth2ProviderApplication.java b/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/configuration/OAuth2ProviderApplication.java
new file mode 100644
index 0000000..ec7c98a
--- /dev/null
+++ b/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/configuration/OAuth2ProviderApplication.java
@@ -0,0 +1,23 @@
+package de.helfenkannjeder.oauth.provider.configuration;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.orm.jpa.EntityScan;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
+import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
+
+/**
+ * @author Valentin Zickner
+ */
+@SpringBootApplication
+@EnableAuthorizationServer
+@EntityScan(basePackages = "de.helfenkannjeder.oauth.provider.domain")
+@EnableJpaRepositories("de.helfenkannjeder.oauth.provider.domain.repository")
+@ComponentScan(basePackages = "de.helfenkannjeder.oauth.provider")
+public class OAuth2ProviderApplication {
+ public static void main(String[] args) {
+ SpringApplication.run(OAuth2ProviderApplication.class, args);
+ }
+
+}
diff --git a/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/configuration/OAuth2ProviderConfiguration.java b/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/configuration/OAuth2ProviderConfiguration.java
new file mode 100644
index 0000000..320a84f
--- /dev/null
+++ b/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/configuration/OAuth2ProviderConfiguration.java
@@ -0,0 +1,100 @@
+package de.helfenkannjeder.oauth.provider.configuration;
+
+import de.helfenkannjeder.oauth.provider.security.OAuthProviderAuthority;
+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.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+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.configurers.AuthorizationServerEndpointsConfigurer;
+import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
+import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
+import org.springframework.security.oauth2.provider.token.TokenStore;
+import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;
+
+/**
+ * @author Valentin Zickner
+ */
+@Configuration
+public class OAuth2ProviderConfiguration extends AuthorizationServerConfigurerAdapter {
+
+ public static final String CLIENT_CREDENTIALS = "client_credentials";
+ public static final String AUTHORIZATION_CODE = "authorization_code";
+ public static final String PASSWORD = "password";
+ public static final String SCOPE_DEFAULT = "default";
+ public static final String REFRESH_TOKEN = "refresh_token";
+
+ private TokenStore tokenStore = new InMemoryTokenStore();
+
+ @Value("${oauth.client.admin.clientId}")
+ private String adminClientId;
+
+ @Value("${oauth.client.admin.secret}")
+ private String adminSecret;
+
+ @Value("${oauth.client.come2help.clientId}")
+ private String come2helpClientId;
+
+ @Value("${oauth.client.come2help.secret}")
+ private String come2helpClientSecret;
+
+ @Autowired
+ @Qualifier("authenticationManagerBean")
+ private AuthenticationManager authenticationManager;
+
+ @Autowired
+ private UserDetailsService userDetailsService;
+
+ @Override
+ public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
+ // @formatter:off
+ clients.inMemory()
+ .withClient(adminClientId)
+ .secret(adminSecret)
+ .authorizedGrantTypes(CLIENT_CREDENTIALS, REFRESH_TOKEN)
+ .scopes(SCOPE_DEFAULT)
+ .authorities(OAuthProviderAuthority.ROLE_ADMIN.getAuthority(),
+ OAuthProviderAuthority.ROLE_USER.getAuthority())
+ .and()
+ .withClient(come2helpClientId)
+ .secret(come2helpClientSecret)
+ .authorizedGrantTypes(AUTHORIZATION_CODE, PASSWORD, REFRESH_TOKEN)
+ .scopes(SCOPE_DEFAULT)
+ .authorities(OAuthProviderAuthority.ROLE_USER.getAuthority());
+ // @formatter:on
+ }
+
+ @Override
+ public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
+ endpoints
+ .authenticationManager(authenticationManager)
+ .userDetailsService(userDetailsService)
+ .tokenStore(tokenStore);
+ }
+
+ @Override
+ public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
+ security.allowFormAuthenticationForClients();
+ }
+
+ @Bean
+ public PasswordEncoder passwordEncoder() {
+ return new BCryptPasswordEncoder();
+ }
+
+ @Bean
+ @Primary
+ public DefaultTokenServices tokenServices() {
+ DefaultTokenServices tokenServices = new DefaultTokenServices();
+ tokenServices.setSupportRefreshToken(true);
+ tokenServices.setTokenStore(this.tokenStore);
+ return tokenServices;
+ }
+}
\ No newline at end of file
diff --git a/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/configuration/ResourceServerConfiguration.java b/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/configuration/ResourceServerConfiguration.java
new file mode 100644
index 0000000..f3b6f5e
--- /dev/null
+++ b/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/configuration/ResourceServerConfiguration.java
@@ -0,0 +1,22 @@
+package de.helfenkannjeder.oauth.provider.configuration;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
+import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
+
+/**
+ * @author Valentin Zickner
+ */
+@Configuration
+@EnableResourceServer
+public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
+ @Override
+ public void configure(HttpSecurity http) throws Exception {
+ // @formatter:off
+ http.authorizeRequests()
+ .antMatchers("/admin/**").hasAuthority("ROLE_ADMIN")
+ .antMatchers("/user/**").authenticated();
+ // @formatter:on
+ }
+}
diff --git a/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/configuration/WebSecurityConfiguration.java b/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/configuration/WebSecurityConfiguration.java
new file mode 100644
index 0000000..2442aa9
--- /dev/null
+++ b/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/configuration/WebSecurityConfiguration.java
@@ -0,0 +1,48 @@
+package de.helfenkannjeder.oauth.provider.configuration;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.security.SecurityProperties;
+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.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.core.userdetails.UserDetailsService;
+import org.springframework.security.crypto.password.PasswordEncoder;
+
+/**
+ * @author Valentin Zickner
+ */
+@Configuration
+@EnableWebSecurity
+@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
+public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
+
+ @Autowired
+ private UserDetailsService userDetailsService;
+
+ @Autowired
+ private PasswordEncoder passwordEncoder;
+
+ @Override
+ protected void configure(AuthenticationManagerBuilder auth) throws Exception {
+ auth
+ .userDetailsService(userDetailsService)
+ .passwordEncoder(passwordEncoder);
+ }
+
+ @Override
+ protected void configure(HttpSecurity http) throws Exception {
+ http.httpBasic();
+ }
+
+ @Override
+ @Bean
+ public AuthenticationManager authenticationManagerBean() throws Exception {
+ return super.authenticationManagerBean();
+ }
+
+}
\ No newline at end of file
diff --git a/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/domain/OAuthUser.java b/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/domain/OAuthUser.java
new file mode 100644
index 0000000..7594b0f
--- /dev/null
+++ b/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/domain/OAuthUser.java
@@ -0,0 +1,50 @@
+package de.helfenkannjeder.oauth.provider.domain;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+
+/**
+ * @author Valentin Zickner
+ */
+@Entity
+public class OAuthUser {
+
+ @Id
+ @GeneratedValue
+ private Long id;
+ private String username;
+ private String password;
+
+ public OAuthUser() {
+ }
+
+ public OAuthUser(String username, String password) {
+ this.username = username;
+ this.password = password;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+}
diff --git a/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/domain/repository/OAuthUserRepository.java b/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/domain/repository/OAuthUserRepository.java
new file mode 100644
index 0000000..344ef9d
--- /dev/null
+++ b/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/domain/repository/OAuthUserRepository.java
@@ -0,0 +1,11 @@
+package de.helfenkannjeder.oauth.provider.domain.repository;
+
+import de.helfenkannjeder.oauth.provider.domain.OAuthUser;
+import org.springframework.data.repository.CrudRepository;
+
+/**
+ * @author Valentin Zickner
+ */
+public interface OAuthUserRepository extends CrudRepository {
+ OAuthUser findOneByUsernameIgnoreCase(String username);
+}
diff --git a/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/exception/UsernameAlreadyExistsException.java b/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/exception/UsernameAlreadyExistsException.java
new file mode 100644
index 0000000..3dccffb
--- /dev/null
+++ b/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/exception/UsernameAlreadyExistsException.java
@@ -0,0 +1,11 @@
+package de.helfenkannjeder.oauth.provider.exception;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+/**
+ * @author Valentin Zickner
+ */
+@ResponseStatus(value = HttpStatus.CONFLICT, reason = "Username already exists.")
+public class UsernameAlreadyExistsException extends RuntimeException {
+}
diff --git a/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/rest/UserController.java b/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/rest/UserController.java
new file mode 100644
index 0000000..93836b5
--- /dev/null
+++ b/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/rest/UserController.java
@@ -0,0 +1,20 @@
+package de.helfenkannjeder.oauth.provider.rest;
+
+import de.helfenkannjeder.oauth.provider.domain.OAuthUser;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.security.Principal;
+
+/**
+ * @author Valentin Zickner
+ */
+@RestController
+public class UserController {
+
+ @RequestMapping("/user/information")
+ public OAuthUser currentUser(Principal user) {
+ return new OAuthUser(user.getName(), null);
+ }
+
+}
diff --git a/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/rest/UserManagementController.java b/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/rest/UserManagementController.java
new file mode 100644
index 0000000..9bf0d9f
--- /dev/null
+++ b/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/rest/UserManagementController.java
@@ -0,0 +1,66 @@
+package de.helfenkannjeder.oauth.provider.rest;
+
+import de.helfenkannjeder.oauth.provider.api.OAuthProviderUserManagementApi;
+import de.helfenkannjeder.oauth.provider.api.dto.UserRequestDto;
+import de.helfenkannjeder.oauth.provider.api.dto.UserResponseDto;
+import de.helfenkannjeder.oauth.provider.domain.OAuthUser;
+import de.helfenkannjeder.oauth.provider.domain.repository.OAuthUserRepository;
+import de.helfenkannjeder.oauth.provider.exception.UsernameAlreadyExistsException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.Valid;
+
+/**
+ * @author Valentin Zickner
+ */
+@RestController
+@RequestMapping("/admin")
+public class UserManagementController implements OAuthProviderUserManagementApi {
+
+ @Autowired
+ private OAuthUserRepository oAuthUserRepository;
+
+ @Autowired
+ private PasswordEncoder passwordEncoder;
+
+ @Override
+ @RequestMapping(value = CREATE, method = RequestMethod.POST)
+ public UserResponseDto create(@Valid @RequestBody UserRequestDto userRequestDto) {
+ assertUserDoesNotExists(userRequestDto);
+
+ String password = passwordEncoder.encode(userRequestDto.getPassword());
+ OAuthUser user = oAuthUserRepository.save(new OAuthUser(userRequestDto.getUsername(), password));
+ return new UserResponseDto(String.valueOf(user.getId()));
+ }
+
+ @Override
+ @RequestMapping(value = UPDATE, method = RequestMethod.PUT)
+ @ResponseStatus(HttpStatus.NO_CONTENT)
+ public void update(@PathVariable("id") String id, @Valid @RequestBody UserRequestDto userRequestDto) {
+ OAuthUser user = oAuthUserRepository.findOne(Long.valueOf(id));
+
+ if (!user.getUsername().equals(userRequestDto.getUsername())) {
+ assertUserDoesNotExists(userRequestDto);
+ }
+
+ user.setUsername(userRequestDto.getUsername());
+ user.setPassword(passwordEncoder.encode(userRequestDto.getPassword()));
+ oAuthUserRepository.save(user);
+ }
+
+ private void assertUserDoesNotExists(@RequestBody UserRequestDto userRequestDto) {
+ if (oAuthUserRepository.findOneByUsernameIgnoreCase(userRequestDto.getUsername()) != null) {
+ throw new UsernameAlreadyExistsException();
+ }
+ }
+
+ @Override
+ @RequestMapping(value = DELETE, method = RequestMethod.DELETE)
+ @ResponseStatus(HttpStatus.NO_CONTENT)
+ public void delete(@PathVariable("id") String id) {
+ oAuthUserRepository.delete(Long.valueOf(id));
+ }
+}
\ No newline at end of file
diff --git a/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/security/OAuthProviderAuthority.java b/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/security/OAuthProviderAuthority.java
new file mode 100644
index 0000000..b0e0f42
--- /dev/null
+++ b/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/security/OAuthProviderAuthority.java
@@ -0,0 +1,14 @@
+package de.helfenkannjeder.oauth.provider.security;
+
+import org.springframework.security.core.GrantedAuthority;
+
+/**
+ * @author Valentin Zickner
+ */
+public enum OAuthProviderAuthority implements GrantedAuthority {
+ ROLE_USER, ROLE_ADMIN;
+
+ public String getAuthority() {
+ return name();
+ }
+}
diff --git a/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/security/OAuthUserDetailsService.java b/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/security/OAuthUserDetailsService.java
new file mode 100644
index 0000000..5946e40
--- /dev/null
+++ b/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/security/OAuthUserDetailsService.java
@@ -0,0 +1,36 @@
+package de.helfenkannjeder.oauth.provider.security;
+
+import de.helfenkannjeder.oauth.provider.domain.OAuthUser;
+import de.helfenkannjeder.oauth.provider.domain.repository.OAuthUserRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+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.Service;
+
+import java.util.Collections;
+
+/**
+ * @author Valentin Zickner
+ */
+@Service
+public class OAuthUserDetailsService implements UserDetailsService {
+
+ private final OAuthUserRepository oAuthUserRepository;
+
+ @Autowired
+ public OAuthUserDetailsService(OAuthUserRepository oAuthUserRepository) {
+ this.oAuthUserRepository = oAuthUserRepository;
+ }
+
+ @Override
+ public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
+ OAuthUser user = oAuthUserRepository.findOneByUsernameIgnoreCase(username);
+ if (user == null) {
+ return null;
+ }
+ return new User(user.getUsername(), user.getPassword(), Collections.singleton(OAuthProviderAuthority.ROLE_USER));
+ }
+
+}
diff --git a/oauth-provider/src/main/resources/application.yml b/oauth-provider/src/main/resources/application.yml
index c2eee0d..1a35644 100644
--- a/oauth-provider/src/main/resources/application.yml
+++ b/oauth-provider/src/main/resources/application.yml
@@ -1 +1,30 @@
-server.port: 8081
\ No newline at end of file
+server.port: 8081
+
+# ===============================
+# = DATA SOURCE
+# ===============================
+spring:
+ datasource:
+ url: jdbc:mysql://localhost:3306/oauth
+ username: oauth
+ password: ZouNjsMYoll7eGHVWm4xHp75nabi4tqdvQTrUQTD6PVjodXy8UlgIXkpA2G2
+
+ testWhileIdle: true
+ validationQuery: SELECT 1
+
+ jpa:
+ show-sql: true
+ hibernate:
+ ddl-auto: update
+ naming-strategy: org.hibernate.cfg.ImprovedNamingStrategy
+ properties:
+ hibernate:
+ dialect: org.hibernate.dialect.MySQL5Dialect
+
+oauth.client:
+ admin:
+ clientId: oauth-provider-admin
+ secret: default-secret
+ come2help:
+ clientId: come2help-web
+ secret: secret
\ No newline at end of file
diff --git a/oauth-provider/src/test/java/de/helfenkannjeder/oauth/provider/MockMvcOAuthLogin.java b/oauth-provider/src/test/java/de/helfenkannjeder/oauth/provider/MockMvcOAuthLogin.java
new file mode 100644
index 0000000..d290c88
--- /dev/null
+++ b/oauth-provider/src/test/java/de/helfenkannjeder/oauth/provider/MockMvcOAuthLogin.java
@@ -0,0 +1,184 @@
+package de.helfenkannjeder.oauth.provider;
+
+import de.helfenkannjeder.oauth.provider.configuration.OAuth2ProviderConfiguration;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.mock.web.MockHttpServletResponse;
+import org.springframework.mock.web.MockHttpSession;
+import org.springframework.security.web.csrf.DefaultCsrfToken;
+import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository;
+import org.springframework.stereotype.Service;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.ResultActions;
+import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+import org.springframework.util.Base64Utils;
+import org.springframework.web.context.WebApplicationContext;
+
+import javax.servlet.Filter;
+import java.util.HashMap;
+import java.util.UUID;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsNull.notNullValue;
+import static org.hamcrest.number.OrderingComparison.greaterThan;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
+
+/**
+ * @author Valentin Zickner
+ */
+@Service
+public class MockMvcOAuthLogin {
+
+ public static final String ENDPOINT_OAUTH_TOKEN = "/oauth/token";
+ public static final String ENDPOINT_OAUTH_AUTHORIZATION = "/oauth/authorize";
+ public static final String ENDPOINT_CONFIRM_ACCESS = "/oauth/confirm_access";
+
+ private static final String DEFAULT_CSRF_TOKEN_ATTR_NAME = HttpSessionCsrfTokenRepository.class
+ .getName().concat(".CSRF_TOKEN");
+
+
+ private WebApplicationContext webApplicationContext;
+ private MockMvc mockMvc;
+
+ @Autowired
+ public MockMvcOAuthLogin(WebApplicationContext webApplicationContext, Filter springSecurityFilterChain) {
+ this.webApplicationContext = webApplicationContext;
+ this.mockMvc = MockMvcBuilders
+ .webAppContextSetup(this.webApplicationContext)
+ .addFilters(springSecurityFilterChain)
+ .build();
+
+ }
+
+ public OAuthInformation getAccessTokenWithClientSecret(String clientId, String secret) throws Exception {
+ return getAccessToken(post(ENDPOINT_OAUTH_TOKEN), OAuth2ProviderConfiguration.CLIENT_CREDENTIALS, clientId, secret);
+ }
+
+ public OAuthInformation getAccessTokenWithAuthorizationCode(String clientId, String secret, String username, String password) throws Exception {
+ MockHttpSession mockSession = new MockHttpSession(webApplicationContext.getServletContext(), UUID.randomUUID().toString());
+
+ String authorization = createBase64Auth(username, password);
+ ResultActions query = mockMvc.perform(
+ get(ENDPOINT_OAUTH_AUTHORIZATION)
+ .session(mockSession)
+ .header("Authorization", authorization)
+ .param("response_type", "code")
+ .param("redirect_uri", "/")
+ .param("client_id", clientId)
+ );
+ MockHttpServletResponse response = query.andReturn().getResponse();
+
+ // needs to confirm access
+ if (response.getStatus() == 200) {
+ query.andExpect(forwardedUrl(ENDPOINT_CONFIRM_ACCESS));
+
+ // Collect CSRF token from session
+ mockMvc.perform(get(ENDPOINT_CONFIRM_ACCESS)
+ .session(mockSession)
+ .header("Authorization", authorization)
+ )
+ .andExpect(status().isOk());
+
+ DefaultCsrfToken csrfToken = (DefaultCsrfToken) mockSession.getAttribute(DEFAULT_CSRF_TOKEN_ATTR_NAME);
+
+ query = mockMvc.perform(
+ post(ENDPOINT_OAUTH_AUTHORIZATION)
+ .session(mockSession)
+ .header("Authorization", authorization)
+ .header(csrfToken.getHeaderName(), csrfToken.getToken())
+ .param("scope.default", "true")
+ .param("user_oauth_approval", "true")
+ .param("authorize", "Authorize")
+ );
+ }
+
+ response = query
+ .andExpect(status().is3xxRedirection())
+ .andReturn().getResponse();
+
+ String content = response.getRedirectedUrl();
+ String code = content.split("=")[1];
+ return getAccessTokenWithAuthorizationCode(clientId, secret, code);
+ }
+
+ private OAuthInformation getAccessTokenWithAuthorizationCode(String clientId, String secret, String code) throws Exception {
+ MockHttpServletRequestBuilder post = post(ENDPOINT_OAUTH_TOKEN)
+ .param("code", code)
+ .param("redirect_uri", "/");
+ return getAccessToken(post, OAuth2ProviderConfiguration.AUTHORIZATION_CODE, clientId, secret);
+ }
+
+ public OAuthInformation getAccessTokenWithPassword(String clientId, String secret, String username, String password) throws Exception {
+ MockHttpServletRequestBuilder post = post(ENDPOINT_OAUTH_TOKEN)
+ .param("username", username)
+ .param("password", password);
+ return getAccessToken(post, OAuth2ProviderConfiguration.PASSWORD, clientId, secret);
+ }
+
+ public OAuthInformation getAccessTokenWithRefreshToken(String clientId, String secret, String refreshToken) throws Exception {
+ MockHttpServletRequestBuilder post = post(ENDPOINT_OAUTH_TOKEN)
+ .param("refresh_token", refreshToken);
+ return getAccessToken(post, OAuth2ProviderConfiguration.REFRESH_TOKEN, clientId, secret);
+ }
+
+ /*
+ * Original from https://github.com/royclarkson/spring-rest-service-oauth/blob/master/src/test/java/hello/GreetingControllerTest.java
+ */
+ private OAuthInformation getAccessToken(MockHttpServletRequestBuilder post, String grantType, String clientId, String secret) throws Exception {
+
+ // @formatter:off
+ String content = mockMvc.perform(post
+ .header("Authorization", createBase64Auth(clientId, secret))
+ .contentType(MediaType.APPLICATION_FORM_URLENCODED)
+ .param("grant_type", grantType)
+ .param("scope", OAuth2ProviderConfiguration.SCOPE_DEFAULT))
+ .andExpect(status().isOk())
+ .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
+ .andExpect(jsonPath("$.access_token", is(notNullValue())))
+ .andExpect(jsonPath("$.token_type", is(equalTo("bearer"))))
+ .andExpect(jsonPath("$.expires_in", is(greaterThan(4000))))
+ .andReturn().getResponse().getContentAsString();
+ // @formatter:on
+
+ ObjectMapper objectMapper = new ObjectMapper();
+ HashMap result = objectMapper.readValue(content, HashMap.class);
+
+ return new OAuthInformation(
+ (String) result.get("access_token"),
+ (String) result.get("refresh_token")
+ );
+ }
+
+ private String createBase64Auth(String username, String password) {
+ return "Basic " + new String(Base64Utils.encode((username + ":" + password).getBytes()));
+ }
+
+
+ public static class OAuthInformation {
+ private final String accessToken;
+ private final String refreshToken;
+
+ public OAuthInformation(String accessToken, String refreshToken) {
+ this.accessToken = accessToken;
+ this.refreshToken = refreshToken;
+ }
+
+ public String getAuthorization() {
+ return "Bearer " + getAccessToken();
+ }
+
+ public String getAccessToken() {
+ return accessToken;
+ }
+
+ public String getRefreshToken() {
+ return refreshToken;
+ }
+ }
+
+}
diff --git a/oauth-provider/src/test/java/de/helfenkannjeder/oauth/provider/rest/AbstractOAuthControllerTest.java b/oauth-provider/src/test/java/de/helfenkannjeder/oauth/provider/rest/AbstractOAuthControllerTest.java
new file mode 100644
index 0000000..ab67281
--- /dev/null
+++ b/oauth-provider/src/test/java/de/helfenkannjeder/oauth/provider/rest/AbstractOAuthControllerTest.java
@@ -0,0 +1,69 @@
+package de.helfenkannjeder.oauth.provider.rest;
+
+import de.helfenkannjeder.oauth.provider.MockMvcOAuthLogin;
+import de.helfenkannjeder.oauth.provider.configuration.OAuth2ProviderApplication;
+import de.helfenkannjeder.oauth.provider.domain.OAuthUser;
+import de.helfenkannjeder.oauth.provider.domain.repository.OAuthUserRepository;
+import org.junit.Before;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.SpringApplicationConfiguration;
+import org.springframework.boot.test.WebIntegrationTest;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+import org.springframework.web.context.WebApplicationContext;
+
+import javax.servlet.Filter;
+
+/**
+ * @author Valentin Zickner
+ */
+@RunWith(SpringJUnit4ClassRunner.class)
+@WebIntegrationTest(randomPort = true)
+@SpringApplicationConfiguration(classes = OAuth2ProviderApplication.class)
+public abstract class AbstractOAuthControllerTest {
+
+ public static final String DEFAULT_USER = "my-default-user";
+ public static final String DEFAULT_PASSWORD = "my-default-password";
+
+ @Autowired
+ protected OAuthUserRepository oAuthUserRepository;
+
+ MockMvc mockMvc;
+
+ @Autowired
+ PasswordEncoder passwordEncoder;
+
+ @Autowired
+ private WebApplicationContext webApplicationContext;
+
+ @Autowired
+ private Filter springSecurityFilterChain;
+
+ @Autowired
+ MockMvcOAuthLogin mockMvcOAuthLogin;
+
+ @Before
+ public void initMockMvc() throws Exception {
+ this.mockMvc = MockMvcBuilders
+ .webAppContextSetup(this.webApplicationContext)
+ .addFilters(springSecurityFilterChain)
+ .build();
+
+ oAuthUserRepository.save(new OAuthUser(AbstractOAuthControllerTest.DEFAULT_USER, passwordEncoder.encode(AbstractOAuthControllerTest.DEFAULT_PASSWORD)));
+ }
+
+ protected String getAuthorizationAdmin() throws Exception {
+ return mockMvcOAuthLogin.getAccessTokenWithClientSecret("oauth-provider-admin", "default-secret").getAuthorization();
+ }
+
+ protected String getAuthorizationDefaultUser() throws Exception {
+ return mockMvcOAuthLogin.getAccessTokenWithAuthorizationCode("come2help-web", "secret", AbstractOAuthControllerTest.DEFAULT_USER, AbstractOAuthControllerTest.DEFAULT_PASSWORD).getAuthorization();
+ }
+
+ protected String getAuthorizationDefaultUserWithPassword() throws Exception {
+ return mockMvcOAuthLogin.getAccessTokenWithPassword("come2help-web", "secret", AbstractOAuthControllerTest.DEFAULT_USER, AbstractOAuthControllerTest.DEFAULT_PASSWORD).getAuthorization();
+ }
+}
diff --git a/oauth-provider/src/test/java/de/helfenkannjeder/oauth/provider/rest/SimpleAuthorizationTest.java b/oauth-provider/src/test/java/de/helfenkannjeder/oauth/provider/rest/SimpleAuthorizationTest.java
new file mode 100644
index 0000000..20ca103
--- /dev/null
+++ b/oauth-provider/src/test/java/de/helfenkannjeder/oauth/provider/rest/SimpleAuthorizationTest.java
@@ -0,0 +1,52 @@
+package de.helfenkannjeder.oauth.provider.rest;
+
+import de.helfenkannjeder.oauth.provider.MockMvcOAuthLogin;
+import org.junit.Test;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.transaction.annotation.Transactional;
+
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+/**
+ * @author Valentin Zickner
+ */
+@Transactional
+@DirtiesContext
+public class SimpleAuthorizationTest extends AbstractOAuthControllerTest {
+
+ @Test
+ public void testAuthorizationAdmin() throws Exception {
+ getAuthorizationAdmin();
+ }
+
+ @Test
+ public void testAuthorizationDefaultUser() throws Exception {
+ getAuthorizationDefaultUser();
+ }
+
+ @Test
+ public void testAuthorizationDefaultUserWithPassword() throws Exception {
+ getAuthorizationDefaultUserWithPassword();
+ }
+
+ @Test
+ public void loginAuthorizationEndpoint_withoutPassword_returnsUnauthorized() throws Exception {
+ // Act
+ mockMvc.perform(get(MockMvcOAuthLogin.ENDPOINT_OAUTH_AUTHORIZATION)
+ .param("response_type", "code")
+ .param("redirect_uri", "/")
+ .param("client_id", "come2help-web")
+ )
+ .andExpect(status().isUnauthorized());
+ }
+
+ @Test
+ public void refreshToken_withRefreshToken_returnsNewToken() throws Exception {
+ // Arrange
+ MockMvcOAuthLogin.OAuthInformation oAuthInformation = mockMvcOAuthLogin.getAccessTokenWithPassword("come2help-web", "secret", AbstractOAuthControllerTest.DEFAULT_USER, AbstractOAuthControllerTest.DEFAULT_PASSWORD);
+
+ // Act + Assert
+ mockMvcOAuthLogin.getAccessTokenWithRefreshToken("come2help-web", "secret", oAuthInformation.getRefreshToken());
+ }
+}
diff --git a/oauth-provider/src/test/java/de/helfenkannjeder/oauth/provider/rest/UserControllerTest.java b/oauth-provider/src/test/java/de/helfenkannjeder/oauth/provider/rest/UserControllerTest.java
new file mode 100644
index 0000000..3de42d0
--- /dev/null
+++ b/oauth-provider/src/test/java/de/helfenkannjeder/oauth/provider/rest/UserControllerTest.java
@@ -0,0 +1,56 @@
+package de.helfenkannjeder.oauth.provider.rest;
+
+import org.junit.Test;
+import org.springframework.http.MediaType;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.transaction.annotation.Transactional;
+
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
+
+/**
+ * @author Valentin Zickner
+ */
+@Transactional
+@DirtiesContext
+public class UserControllerTest extends AbstractOAuthControllerTest {
+
+ @Test
+ public void currentUser_withNormalUser_expectUsername() throws Exception {
+ // Act + Assert
+ this.mockMvc.perform(get("/user/information")
+ .header("Authorization", getAuthorizationDefaultUser())
+ .accept(MediaType.APPLICATION_JSON_UTF8)
+ )
+ .andExpect(status().isOk())
+ .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
+ .andExpect(jsonPath("$").isMap())
+ .andExpect(jsonPath("$.username").value(DEFAULT_USER));
+ }
+
+ @Test
+ public void currentUser_withNormalUserAndPasswordAuthentication_expectUsername() throws Exception {
+ // Act + Assert
+ this.mockMvc.perform(get("/user/information")
+ .header("Authorization", getAuthorizationDefaultUserWithPassword())
+ .accept(MediaType.APPLICATION_JSON_UTF8)
+ )
+ .andExpect(status().isOk())
+ .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
+ .andExpect(jsonPath("$").isMap())
+ .andExpect(jsonPath("$.username").value(DEFAULT_USER));
+ }
+
+ @Test
+ public void currentUser_withAdmin_expectNoUsername() throws Exception {
+ // Act + Assert
+ this.mockMvc.perform(get("/user/information")
+ .header("Authorization", getAuthorizationAdmin())
+ .accept(MediaType.APPLICATION_JSON_UTF8)
+ )
+ .andExpect(status().isOk())
+ .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
+ .andExpect(jsonPath("$").isMap())
+ .andExpect(jsonPath("$.username").value("oauth-provider-admin"));
+ }
+}
\ No newline at end of file
diff --git a/oauth-provider/src/test/java/de/helfenkannjeder/oauth/provider/rest/UserManagementControllerTest.java b/oauth-provider/src/test/java/de/helfenkannjeder/oauth/provider/rest/UserManagementControllerTest.java
new file mode 100644
index 0000000..7376912
--- /dev/null
+++ b/oauth-provider/src/test/java/de/helfenkannjeder/oauth/provider/rest/UserManagementControllerTest.java
@@ -0,0 +1,262 @@
+package de.helfenkannjeder.oauth.provider.rest;
+
+import de.helfenkannjeder.oauth.provider.api.OAuthProviderUserManagementApi;
+import de.helfenkannjeder.oauth.provider.api.dto.UserRequestDto;
+import de.helfenkannjeder.oauth.provider.api.dto.UserResponseDto;
+import de.helfenkannjeder.oauth.provider.domain.OAuthUser;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.junit.Before;
+import org.junit.Test;
+import org.springframework.http.MediaType;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.web.servlet.MvcResult;
+import org.springframework.transaction.annotation.Transactional;
+
+import static junit.framework.TestCase.assertNotNull;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
+
+/**
+ * @author Valentin Zickner
+ */
+@Transactional
+@DirtiesContext
+public class UserManagementControllerTest extends AbstractOAuthControllerTest {
+
+ public static final String RESOURCE_PREFIX = "/admin";
+
+ private ObjectMapper objectMapper = new ObjectMapper();
+
+ private UserRequestDto userRequestDto;
+
+ @Before
+ public void setUp() throws Exception {
+ userRequestDto = new UserRequestDto("my-user", "my-password");
+ }
+
+ @Test
+ public void create_withAdminAndUsernameAndPassword_returnsNewUserId() throws Exception {
+ // Act
+ MvcResult result = this.mockMvc.perform(post(RESOURCE_PREFIX + OAuthProviderUserManagementApi.CREATE)
+ .header("Authorization", getAuthorizationAdmin())
+ .contentType(MediaType.APPLICATION_JSON_UTF8)
+ .content(objectMapper.writeValueAsString(userRequestDto))
+ .accept(MediaType.APPLICATION_JSON_UTF8)
+ )
+ .andExpect(status().isOk())
+ .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON_UTF8))
+ .andExpect(jsonPath("$").isMap())
+ .andExpect(jsonPath("$.id").isNotEmpty())
+ .andReturn();
+
+ // Assert
+ assertNotNull(result);
+ UserResponseDto userResponseDto = objectMapper.readValue(result.getResponse().getContentAsString(), UserResponseDto.class);
+ String userId = userResponseDto.getId();
+ assertNotNull(userId);
+ OAuthUser oAuthUser = oAuthUserRepository.findOne(Long.valueOf(userId));
+ assertEquals(userRequestDto.getUsername(), oAuthUser.getUsername());
+ assertTrue(passwordEncoder.matches(userRequestDto.getPassword(), oAuthUser.getPassword()));
+ }
+
+ @Test
+ public void create_withAdminAndWithoutContent_returnsNoNewUser() throws Exception {
+ // Act
+ this.mockMvc.perform(post(RESOURCE_PREFIX + OAuthProviderUserManagementApi.CREATE)
+ .header("Authorization", getAuthorizationAdmin())
+ .contentType(MediaType.APPLICATION_JSON_UTF8)
+ .content("{}")
+ .accept(MediaType.APPLICATION_JSON_UTF8)
+ )
+ .andExpect(status().isBadRequest());
+ }
+
+ @Test
+ public void create_withAdminAndWithEmptyUsernameAndPassword_returnsNoNewUser() throws Exception {
+ // Act
+ this.mockMvc.perform(post(RESOURCE_PREFIX + OAuthProviderUserManagementApi.CREATE)
+ .header("Authorization", getAuthorizationAdmin())
+ .contentType(MediaType.APPLICATION_JSON_UTF8)
+ .content("{\"username\":\"\",\"password\":\"\"}")
+ .accept(MediaType.APPLICATION_JSON_UTF8)
+ )
+ .andExpect(status().isBadRequest());
+ }
+
+ @Test
+ public void create_withAdminAndDuplicateUsernameInLowerCase_returnsConflict() throws Exception {
+ // Arrange
+ userRequestDto.setUsername(DEFAULT_USER.toLowerCase());
+
+ // Act
+ this.mockMvc.perform(post(RESOURCE_PREFIX + OAuthProviderUserManagementApi.CREATE)
+ .header("Authorization", getAuthorizationAdmin())
+ .contentType(MediaType.APPLICATION_JSON_UTF8)
+ .content(objectMapper.writeValueAsString(userRequestDto))
+ .accept(MediaType.APPLICATION_JSON_UTF8)
+ )
+
+ // Assert
+ .andExpect(status().isConflict());
+ }
+
+ @Test
+ public void create_withAdminAndDuplicateUsernameInUpperCase_returnsConflict() throws Exception {
+ // Arrange
+ userRequestDto.setUsername(DEFAULT_USER.toUpperCase());
+
+ // Act
+ this.mockMvc.perform(post(RESOURCE_PREFIX + OAuthProviderUserManagementApi.CREATE)
+ .header("Authorization", getAuthorizationAdmin())
+ .contentType(MediaType.APPLICATION_JSON_UTF8)
+ .content(objectMapper.writeValueAsString(userRequestDto))
+ .accept(MediaType.APPLICATION_JSON_UTF8)
+ )
+
+ // Assert
+ .andExpect(status().isConflict());
+ }
+
+ @Test
+ public void create_withNotAdmin_returns403() throws Exception {
+ // Act + Assert
+ this.mockMvc.perform(post(RESOURCE_PREFIX + OAuthProviderUserManagementApi.CREATE)
+ .header("Authorization", getAuthorizationDefaultUser())
+ .contentType(MediaType.APPLICATION_JSON_UTF8)
+ .content(objectMapper.writeValueAsString(userRequestDto))
+ .accept(MediaType.APPLICATION_JSON_UTF8)
+ ).andExpect(status().isForbidden());
+ }
+
+ @Test
+ public void create_withNoAuthentication_returns401() throws Exception {
+ // Act + Assert
+ this.mockMvc.perform(post(RESOURCE_PREFIX + OAuthProviderUserManagementApi.CREATE)
+ .contentType(MediaType.APPLICATION_JSON_UTF8)
+ .content(objectMapper.writeValueAsString(userRequestDto))
+ .accept(MediaType.APPLICATION_JSON_UTF8)
+ ).andExpect(status().isUnauthorized());
+ }
+
+ @Test
+ public void update_withAdminLoggedInAndNewUserInformation_verifyUserIsChanged() throws Exception {
+ // Arrange
+ Long userId = oAuthUserRepository.findOneByUsernameIgnoreCase(DEFAULT_USER).getId();
+
+ // Act
+ this.mockMvc.perform(put(RESOURCE_PREFIX + OAuthProviderUserManagementApi.UPDATE.replace("{id}", userId.toString()))
+ .header("Authorization", getAuthorizationAdmin())
+ .contentType(MediaType.APPLICATION_JSON_UTF8)
+ .content(objectMapper.writeValueAsString(userRequestDto))
+ .accept(MediaType.APPLICATION_JSON_UTF8)
+ )
+ .andExpect(status().isNoContent());
+
+ // Assert
+ OAuthUser oAuthUser = oAuthUserRepository.findOne(userId);
+ assertEquals(userRequestDto.getUsername(), oAuthUser.getUsername());
+ assertTrue(passwordEncoder.matches(userRequestDto.getPassword(), oAuthUser.getPassword()));
+ }
+
+ @Test
+ public void update_withAdminLoggedInAndAlreadyExistingUsername_verifyUserIsNotChanged() throws Exception {
+ // Arrange
+ oAuthUserRepository.save(new OAuthUser(userRequestDto.getUsername(), "password"));
+
+ Long userId = oAuthUserRepository.findOneByUsernameIgnoreCase(DEFAULT_USER).getId();
+
+ // Act
+ this.mockMvc.perform(put(RESOURCE_PREFIX + OAuthProviderUserManagementApi.UPDATE.replace("{id}", userId.toString()))
+ .header("Authorization", getAuthorizationAdmin())
+ .contentType(MediaType.APPLICATION_JSON_UTF8)
+ .content(objectMapper.writeValueAsString(userRequestDto))
+ .accept(MediaType.APPLICATION_JSON_UTF8)
+ )
+ .andExpect(status().isConflict());
+
+ // Assert
+ assertUserUnchanged(userId);
+ }
+
+ private void assertUserUnchanged(Long userId) {
+ OAuthUser oAuthUser = oAuthUserRepository.findOne(userId);
+ assertEquals(DEFAULT_USER, oAuthUser.getUsername());
+ assertTrue(passwordEncoder.matches(DEFAULT_PASSWORD, oAuthUser.getPassword()));
+ }
+
+ @Test
+ public void update_withAdminLoggedInOnlyChangingPassword_verifyUserIsChanged() throws Exception {
+ // Arrange
+ Long userId = oAuthUserRepository.findOneByUsernameIgnoreCase(DEFAULT_USER).getId();
+ userRequestDto.setUsername(DEFAULT_USER);
+
+ // Act
+ this.mockMvc.perform(put(RESOURCE_PREFIX + OAuthProviderUserManagementApi.UPDATE.replace("{id}", userId.toString()))
+ .header("Authorization", getAuthorizationAdmin())
+ .contentType(MediaType.APPLICATION_JSON_UTF8)
+ .content(objectMapper.writeValueAsString(userRequestDto))
+ .accept(MediaType.APPLICATION_JSON_UTF8)
+ )
+ .andExpect(status().isNoContent());
+
+ // Assert
+ OAuthUser oAuthUser = oAuthUserRepository.findOne(userId);
+ assertEquals(DEFAULT_USER, oAuthUser.getUsername());
+ assertTrue(passwordEncoder.matches(userRequestDto.getPassword(), oAuthUser.getPassword()));
+ }
+
+
+ @Test
+ public void update_withAdminLoggedInEmptyObject_verifyUserIsNotChanged() throws Exception {
+ // Arrange
+ Long userId = oAuthUserRepository.findOneByUsernameIgnoreCase(DEFAULT_USER).getId();
+
+ // Act
+ this.mockMvc.perform(put(RESOURCE_PREFIX + OAuthProviderUserManagementApi.UPDATE.replace("{id}", userId.toString()))
+ .header("Authorization", getAuthorizationAdmin())
+ .contentType(MediaType.APPLICATION_JSON_UTF8)
+ .content("{}")
+ .accept(MediaType.APPLICATION_JSON_UTF8)
+ )
+ .andExpect(status().isBadRequest());
+
+ // Assert
+ assertUserUnchanged(userId);
+ }
+
+ @Test
+ public void delete_withAdminLoggedIn_verifyUserIsRemoved() throws Exception {
+ // Arrange
+ Long userId = oAuthUserRepository.findOneByUsernameIgnoreCase(DEFAULT_USER).getId();
+
+ // Act
+ this.mockMvc.perform(delete(RESOURCE_PREFIX + OAuthProviderUserManagementApi.DELETE.replace("{id}", userId.toString()))
+ .header("Authorization", getAuthorizationAdmin())
+ )
+ .andExpect(status().isNoContent());
+
+ // Assert
+ OAuthUser oAuthUser = oAuthUserRepository.findOne(userId);
+ assertNull(oAuthUser);
+ }
+
+ @Test
+ public void delete_withNormalUserLoggedIn_verifyUserIsNotRemoved() throws Exception {
+ // Arrange
+ Long userId = oAuthUserRepository.findOneByUsernameIgnoreCase(DEFAULT_USER).getId();
+
+ // Act
+ this.mockMvc.perform(delete(RESOURCE_PREFIX + OAuthProviderUserManagementApi.DELETE.replace("{id}", userId.toString()))
+ .header("Authorization", getAuthorizationDefaultUser())
+ )
+ .andExpect(status().isForbidden());
+
+ // Assert
+ assertUserUnchanged(userId);
+ }
+}
\ No newline at end of file
diff --git a/oauth-provider/src/test/java/de/helfenkannjeder/oauth/provider/service/OAuthUserDetailsServiceTest.java b/oauth-provider/src/test/java/de/helfenkannjeder/oauth/provider/service/OAuthUserDetailsServiceTest.java
new file mode 100644
index 0000000..ef7bc65
--- /dev/null
+++ b/oauth-provider/src/test/java/de/helfenkannjeder/oauth/provider/service/OAuthUserDetailsServiceTest.java
@@ -0,0 +1,46 @@
+package de.helfenkannjeder.oauth.provider.service;
+
+import de.helfenkannjeder.oauth.provider.domain.OAuthUser;
+import de.helfenkannjeder.oauth.provider.domain.repository.OAuthUserRepository;
+import de.helfenkannjeder.oauth.provider.security.OAuthUserDetailsService;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.springframework.security.core.userdetails.UserDetails;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Mockito.when;
+
+/**
+ * @author Valentin Zickner
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class OAuthUserDetailsServiceTest {
+
+ private OAuthUserDetailsService oAuthUserDetailsService;
+
+ @Mock
+ private OAuthUserRepository oAuthUserRepository;
+
+ @Before
+ public void setUp() throws Exception {
+ when(oAuthUserRepository.findOneByUsernameIgnoreCase("my-user")).thenReturn(new OAuthUser("my-user", "$2a$10$mXEdVKm16/vj/JyE.MgQ..UBa0p4rF1JYeGvLzvOJykact6UPVRx."));
+ oAuthUserDetailsService = new OAuthUserDetailsService(oAuthUserRepository);
+ }
+
+ @Test
+ public void loadUserByUsername_withMockedUser_verifyUserDetails() throws Exception {
+ // Arrange
+
+ // Act
+ UserDetails userDetails = oAuthUserDetailsService.loadUserByUsername("my-user");
+
+ // Assert
+ assertNotNull(userDetails);
+ assertEquals("my-user", userDetails.getUsername());
+ assertEquals("$2a$10$mXEdVKm16/vj/JyE.MgQ..UBa0p4rF1JYeGvLzvOJykact6UPVRx.", userDetails.getPassword());
+ }
+}
\ No newline at end of file
diff --git a/oauth-provider/src/test/resources/application.yml b/oauth-provider/src/test/resources/application.yml
new file mode 100644
index 0000000..c10ccaa
--- /dev/null
+++ b/oauth-provider/src/test/resources/application.yml
@@ -0,0 +1,22 @@
+# ===============================
+# = DATA SOURCE
+# ===============================
+spring:
+ datasource:
+ url: jdbc:h2:mem:AZ;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
+ driverClassName: org.h2.Driver
+ username: sa
+ password:
+
+ jpa:
+ properties:
+ hibernate:
+ dialect: org.hibernate.dialect.H2Dialect
+
+oauth.client:
+ admin:
+ clientId: oauth-provider-admin
+ secret: default-secret
+ come2help:
+ clientId: come2help-web
+ secret: secret
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index aeab6f9..d444f0a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -10,11 +10,13 @@
pom
+ 1.8
4.12
+ 5.2.4.Final
- oauth-provider
oauth-provider-api
+ oauth-provider