From f2cdc441ef717a046fc41264a16f807ffd32e6e8 Mon Sep 17 00:00:00 2001 From: Valentin Zickner Date: Wed, 1 Jun 2016 21:36:31 +0200 Subject: [PATCH 01/17] Add basic oauth provider with static user credentials. --- oauth-provider/oauth-provider.iml | 15 ++++-- oauth-provider/pom.xml | 4 ++ .../oauth/provider/SampleController.java | 25 ---------- .../OAuth2ProviderApplication.java | 19 +++++++ .../OAuth2ProviderConfiguration.java | 50 +++++++++++++++++++ .../rest/OAuthInformationController.java | 20 ++++++++ 6 files changed, 104 insertions(+), 29 deletions(-) delete mode 100644 oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/SampleController.java create mode 100644 oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/configuration/OAuth2ProviderApplication.java create mode 100644 oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/configuration/OAuth2ProviderConfiguration.java create mode 100644 oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/rest/OAuthInformationController.java diff --git a/oauth-provider/oauth-provider.iml b/oauth-provider/oauth-provider.iml index 308bafe..644044f 100644 --- a/oauth-provider/oauth-provider.iml +++ b/oauth-provider/oauth-provider.iml @@ -21,7 +21,6 @@ - @@ -38,10 +37,18 @@ - - - + + + + + + + + + + + \ No newline at end of file diff --git a/oauth-provider/pom.xml b/oauth-provider/pom.xml index 85a97d7..2b0f857 100644 --- a/oauth-provider/pom.xml +++ b/oauth-provider/pom.xml @@ -19,5 +19,9 @@ org.springframework.boot spring-boot-starter-web + + org.springframework.security.oauth + spring-security-oauth2 + 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..561fc5c --- /dev/null +++ b/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/configuration/OAuth2ProviderApplication.java @@ -0,0 +1,19 @@ +package de.helfenkannjeder.oauth.provider.configuration; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; + +/** + * @author Valentin Zickner + */ +@SpringBootApplication +@EnableAuthorizationServer +@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..b486895 --- /dev/null +++ b/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/configuration/OAuth2ProviderConfiguration.java @@ -0,0 +1,50 @@ +package de.helfenkannjeder.oauth.provider.configuration; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +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.TokenStore; +import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore; + +/** + * @author Valentin Zickner + */ +@Configuration +public class OAuth2ProviderConfiguration extends AuthorizationServerConfigurerAdapter { + + private TokenStore tokenStore = new InMemoryTokenStore(); + + @Override + public void configure(ClientDetailsServiceConfigurer clients) throws Exception { + clients.inMemory().withClient("come2help-web") + .resourceIds("come2help") + .authorizedGrantTypes("authorization_code") + .authorities("CLIENT") + .scopes("read", "write") + .secret("secret"); + } + + @Override + public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { + endpoints.tokenStore(tokenStore); + } + + @Override + public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { + security.allowFormAuthenticationForClients(); + } + + @Autowired + public void configureGlobal(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception { + authenticationManagerBuilder + .inMemoryAuthentication() + .withUser("user") + .password("password") + .roles("USER"); + } + +} \ No newline at end of file diff --git a/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/rest/OAuthInformationController.java b/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/rest/OAuthInformationController.java new file mode 100644 index 0000000..fe47337 --- /dev/null +++ b/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/rest/OAuthInformationController.java @@ -0,0 +1,20 @@ +package de.helfenkannjeder.oauth.provider.rest; + +import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.security.Principal; + +/** + * @author Valentin Zickner + */ +@RestController +@EnableResourceServer +public class OAuthInformationController { + + @RequestMapping("/userInformation") + public Principal user(Principal user) { + return user; + } +} \ No newline at end of file From f13268eddc1a4605e981ad884194b4b47617f0f7 Mon Sep 17 00:00:00 2001 From: Valentin Zickner Date: Fri, 3 Jun 2016 17:13:33 +0200 Subject: [PATCH 02/17] Implement basic endpoint with test for user creation. --- common.iml | 12 ++++ oauth-provider-api/oauth-provider-api.iml | 9 +-- oauth-provider/oauth-provider.iml | 33 ++++++++++- oauth-provider/pom.xml | 27 +++++++++ .../OAuth2ProviderApplication.java | 2 + .../configuration/SecurityConfiguration.java | 18 ++++++ .../rest/OAuthInformationController.java | 20 ------- .../rest/UserManagementController.java | 41 ++++++++++++++ .../rest/UserManagementControllerTest.java | 55 +++++++++++++++++++ pom.xml | 3 +- 10 files changed, 188 insertions(+), 32 deletions(-) create mode 100644 common.iml create mode 100644 oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/configuration/SecurityConfiguration.java delete mode 100644 oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/rest/OAuthInformationController.java create mode 100644 oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/rest/UserManagementController.java create mode 100644 oauth-provider/src/test/java/de/helfenkannjeder/oauth/provider/rest/UserManagementControllerTest.java 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..85a0502 100644 --- a/oauth-provider-api/oauth-provider-api.iml +++ b/oauth-provider-api/oauth-provider-api.iml @@ -5,18 +5,11 @@ - - + - - - - - - diff --git a/oauth-provider/oauth-provider.iml b/oauth-provider/oauth-provider.iml index 644044f..07286eb 100644 --- a/oauth-provider/oauth-provider.iml +++ b/oauth-provider/oauth-provider.iml @@ -1,14 +1,27 @@ - + + + + + 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,7 +30,6 @@ - @@ -50,5 +62,20 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/oauth-provider/pom.xml b/oauth-provider/pom.xml index 2b0f857..e9db879 100644 --- a/oauth-provider/pom.xml +++ b/oauth-provider/pom.xml @@ -6,6 +6,12 @@ de.helfenkannjeder.common oauth-provider + + 1.8 + 0.0.1-SNAPSHOT + 4.12 + + org.springframework.boot spring-boot-starter-parent @@ -23,5 +29,26 @@ org.springframework.security.oauth spring-security-oauth2 + + de.helfenkannjeder.common + oauth-provider-api + ${common.version} + + + org.springframework.boot + spring-boot-starter-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/configuration/OAuth2ProviderApplication.java b/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/configuration/OAuth2ProviderApplication.java index 561fc5c..fa3f695 100644 --- 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 @@ -4,11 +4,13 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.ComponentScan; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; +import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; /** * @author Valentin Zickner */ @SpringBootApplication +@EnableResourceServer @EnableAuthorizationServer @ComponentScan(basePackages = "de.helfenkannjeder.oauth.provider") public class OAuth2ProviderApplication { diff --git a/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/configuration/SecurityConfiguration.java b/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/configuration/SecurityConfiguration.java new file mode 100644 index 0000000..75fc9dc --- /dev/null +++ b/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/configuration/SecurityConfiguration.java @@ -0,0 +1,18 @@ +package de.helfenkannjeder.oauth.provider.configuration; + +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.WebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; + +/** + * @author Valentin Zickner + */ +@Configuration +public class SecurityConfiguration extends WebSecurityConfigurerAdapter { + @Override + public void configure(WebSecurity web) throws Exception { + web.ignoring() + .antMatchers("/users/**") + .antMatchers("/users"); + } +} diff --git a/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/rest/OAuthInformationController.java b/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/rest/OAuthInformationController.java deleted file mode 100644 index fe47337..0000000 --- a/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/rest/OAuthInformationController.java +++ /dev/null @@ -1,20 +0,0 @@ -package de.helfenkannjeder.oauth.provider.rest; - -import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -import java.security.Principal; - -/** - * @author Valentin Zickner - */ -@RestController -@EnableResourceServer -public class OAuthInformationController { - - @RequestMapping("/userInformation") - public Principal user(Principal user) { - return user; - } -} \ No newline at end of file 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..58fb6d8 --- /dev/null +++ b/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/rest/UserManagementController.java @@ -0,0 +1,41 @@ +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 org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.security.Principal; + +/** + * @author Valentin Zickner + */ +@RestController +public class UserManagementController implements OAuthProviderUserManagementApi { + + @RequestMapping("/userInformation") + public Principal currentUser(Principal user) { + return user; + } + + @Override + @RequestMapping(value = CREATE, method = RequestMethod.POST) + public UserResponseDto create(UserRequestDto userRequestDto) { + return new UserResponseDto(null); + } + + @Override + @RequestMapping(value = UPDATE, method = RequestMethod.PUT) + public void update(@RequestParam("id") String id, UserRequestDto userRequestDto) { + + } + + @Override + @RequestMapping(value = DELETE, method = RequestMethod.DELETE) + public void delete(@RequestParam("id") String id) { + + } +} \ 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..dc8a13d --- /dev/null +++ b/oauth-provider/src/test/java/de/helfenkannjeder/oauth/provider/rest/UserManagementControllerTest.java @@ -0,0 +1,55 @@ +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.configuration.OAuth2ProviderApplication; +import org.codehaus.jackson.map.ObjectMapper; +import org.junit.Before; +import org.junit.Test; +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.http.MediaType; +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 static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +/** + * @author Valentin Zickner + */ +@RunWith(SpringJUnit4ClassRunner.class) +@WebIntegrationTest +@SpringApplicationConfiguration(classes = OAuth2ProviderApplication.class) +public class UserManagementControllerTest { + + @Autowired + private WebApplicationContext webApplicationContext; + + private MockMvc mockMvc; + private ObjectMapper objectMapper = new ObjectMapper(); + + @Before + public void setUp() throws Exception { + this.mockMvc = MockMvcBuilders.webAppContextSetup(this.webApplicationContext).build(); + } + + @Test + public void create_withUsernameAndPassword_returnsNewUserId() throws Exception { + UserRequestDto user = new UserRequestDto("my-user", "my-password"); + this.mockMvc.perform(post(OAuthProviderUserManagementApi.CREATE) + .contentType(MediaType.APPLICATION_JSON_UTF8) + .content(objectMapper.writeValueAsString(user)) + .accept(MediaType.APPLICATION_JSON_UTF8) + ) + .andExpect(status().isOk()) + .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON_UTF8)) + .andExpect(jsonPath("$").isMap()) + .andExpect(jsonPath("$.id").isNotEmpty()) + .andReturn(); + } +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index aeab6f9..0cfe084 100644 --- a/pom.xml +++ b/pom.xml @@ -10,11 +10,12 @@ pom + 1.8 4.12 - oauth-provider oauth-provider-api + oauth-provider From e2ad91a1b5834db62f9a556282b9d2e6fced55cd Mon Sep 17 00:00:00 2001 From: Valentin Zickner Date: Fri, 3 Jun 2016 18:19:28 +0200 Subject: [PATCH 03/17] Implement creation of new users. --- oauth-provider/oauth-provider.iml | 30 ++++++++++- oauth-provider/pom.xml | 17 +++++++ .../OAuth2ProviderApplication.java | 4 ++ .../OAuth2ProviderConfiguration.java | 7 +++ .../configuration/SecurityConfiguration.java | 3 +- .../oauth/provider/domain/OAuthUser.java | 50 +++++++++++++++++++ .../repository/OAuthUserRepository.java | 10 ++++ .../rest/UserManagementController.java | 26 +++++++--- .../src/main/resources/application.yml | 23 ++++++++- .../rest/UserManagementControllerTest.java | 30 ++++++++++- .../src/test/resources/application.yml | 14 ++++++ 11 files changed, 201 insertions(+), 13 deletions(-) create mode 100644 oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/domain/OAuthUser.java create mode 100644 oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/domain/repository/OAuthUserRepository.java create mode 100644 oauth-provider/src/test/resources/application.yml diff --git a/oauth-provider/oauth-provider.iml b/oauth-provider/oauth-provider.iml index 07286eb..3105275 100644 --- a/oauth-provider/oauth-provider.iml +++ b/oauth-provider/oauth-provider.iml @@ -19,6 +19,7 @@ + @@ -30,7 +31,6 @@ - @@ -42,7 +42,6 @@ - @@ -65,6 +64,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/oauth-provider/pom.xml b/oauth-provider/pom.xml index e9db879..c835b28 100644 --- a/oauth-provider/pom.xml +++ b/oauth-provider/pom.xml @@ -10,6 +10,8 @@ 1.8 0.0.1-SNAPSHOT 4.12 + 5.1.38 + 1.4.182 @@ -34,6 +36,21 @@ oauth-provider-api ${common.version} + + org.springframework.boot + spring-boot-starter-data-jpa + + + mysql + mysql-connector-java + ${mysql-connector-java.version} + + + com.h2database + h2 + ${h2.version} + test + org.springframework.boot spring-boot-starter-test 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 index fa3f695..81f927c 100644 --- 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 @@ -2,7 +2,9 @@ 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; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; @@ -12,6 +14,8 @@ @SpringBootApplication @EnableResourceServer @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) { 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 index b486895..b5db06e 100644 --- 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 @@ -1,8 +1,11 @@ package de.helfenkannjeder.oauth.provider.configuration; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +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; @@ -47,4 +50,8 @@ public void configureGlobal(AuthenticationManagerBuilder authenticationManagerBu .roles("USER"); } + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } } \ No newline at end of file diff --git a/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/configuration/SecurityConfiguration.java b/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/configuration/SecurityConfiguration.java index 75fc9dc..7a4a553 100644 --- a/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/configuration/SecurityConfiguration.java +++ b/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/configuration/SecurityConfiguration.java @@ -12,7 +12,6 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Override public void configure(WebSecurity web) throws Exception { web.ignoring() - .antMatchers("/users/**") - .antMatchers("/users"); + .antMatchers("/admin/**"); } } 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..7e24f57 --- /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 String id; + private String username; + private String password; + + public OAuthUser() { + } + + public OAuthUser(String username, String password) { + this.username = username; + this.password = password; + } + + public String getId() { + return id; + } + + public void setId(String 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..9a6b7a2 --- /dev/null +++ b/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/domain/repository/OAuthUserRepository.java @@ -0,0 +1,10 @@ +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 { +} 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 index 58fb6d8..391f3db 100644 --- 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 @@ -3,10 +3,12 @@ import de.helfenkannjeder.oauth.provider.api.OAuthProviderUserManagementApi; import de.helfenkannjeder.oauth.provider.api.dto.UserRequestDto; import de.helfenkannjeder.oauth.provider.api.dto.UserResponseDto; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; +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.http.HttpStatus; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.web.bind.annotation.*; import java.security.Principal; @@ -14,8 +16,15 @@ * @author Valentin Zickner */ @RestController +@RequestMapping("/admin") public class UserManagementController implements OAuthProviderUserManagementApi { + @Autowired + private OAuthUserRepository oAuthUserRepository; + + @Autowired + private PasswordEncoder passwordEncoder; + @RequestMapping("/userInformation") public Principal currentUser(Principal user) { return user; @@ -23,18 +32,23 @@ public Principal currentUser(Principal user) { @Override @RequestMapping(value = CREATE, method = RequestMethod.POST) - public UserResponseDto create(UserRequestDto userRequestDto) { - return new UserResponseDto(null); + public UserResponseDto create(@RequestBody UserRequestDto userRequestDto) { + // TODO: Test if user already exists + String password = passwordEncoder.encode(userRequestDto.getPassword()); + OAuthUser user = oAuthUserRepository.save(new OAuthUser(userRequestDto.getUsername(), password)); + return new UserResponseDto(user.getId()); } @Override @RequestMapping(value = UPDATE, method = RequestMethod.PUT) + @ResponseStatus(HttpStatus.NO_CONTENT) public void update(@RequestParam("id") String id, UserRequestDto userRequestDto) { } @Override @RequestMapping(value = DELETE, method = RequestMethod.DELETE) + @ResponseStatus(HttpStatus.NO_CONTENT) public void delete(@RequestParam("id") String id) { } diff --git a/oauth-provider/src/main/resources/application.yml b/oauth-provider/src/main/resources/application.yml index c2eee0d..78861b2 100644 --- a/oauth-provider/src/main/resources/application.yml +++ b/oauth-provider/src/main/resources/application.yml @@ -1 +1,22 @@ -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 \ 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 index dc8a13d..8cbbaf0 100644 --- 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 @@ -2,7 +2,10 @@ 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.configuration.OAuth2ProviderApplication; +import de.helfenkannjeder.oauth.provider.domain.OAuthUser; +import de.helfenkannjeder.oauth.provider.domain.repository.OAuthUserRepository; import org.codehaus.jackson.map.ObjectMapper; import org.junit.Before; import org.junit.Test; @@ -11,11 +14,16 @@ import org.springframework.boot.test.SpringApplicationConfiguration; import org.springframework.boot.test.WebIntegrationTest; import org.springframework.http.MediaType; +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.MvcResult; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; +import static junit.framework.TestCase.assertNotNull; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; @@ -27,12 +35,20 @@ @SpringApplicationConfiguration(classes = OAuth2ProviderApplication.class) public class UserManagementControllerTest { + public static final String RESOURCE_PREFIX = "/admin"; + @Autowired private WebApplicationContext webApplicationContext; private MockMvc mockMvc; private ObjectMapper objectMapper = new ObjectMapper(); + @Autowired + private OAuthUserRepository oAuthUserRepository; + + @Autowired + private PasswordEncoder passwordEncoder; + @Before public void setUp() throws Exception { this.mockMvc = MockMvcBuilders.webAppContextSetup(this.webApplicationContext).build(); @@ -40,8 +56,10 @@ public void setUp() throws Exception { @Test public void create_withUsernameAndPassword_returnsNewUserId() throws Exception { - UserRequestDto user = new UserRequestDto("my-user", "my-password"); - this.mockMvc.perform(post(OAuthProviderUserManagementApi.CREATE) + String username = "my-user"; + String password = "my-password"; + UserRequestDto user = new UserRequestDto(username, password); + MvcResult result = this.mockMvc.perform(post(RESOURCE_PREFIX + OAuthProviderUserManagementApi.CREATE) .contentType(MediaType.APPLICATION_JSON_UTF8) .content(objectMapper.writeValueAsString(user)) .accept(MediaType.APPLICATION_JSON_UTF8) @@ -51,5 +69,13 @@ public void create_withUsernameAndPassword_returnsNewUserId() throws Exception { .andExpect(jsonPath("$").isMap()) .andExpect(jsonPath("$.id").isNotEmpty()) .andReturn(); + + assertNotNull(result); + UserResponseDto userResponseDto = objectMapper.readValue(result.getResponse().getContentAsString(), UserResponseDto.class); + String userId = userResponseDto.getId(); + assertNotNull(userId); + OAuthUser oAuthUser = oAuthUserRepository.findOne(userId); + assertEquals(username, oAuthUser.getUsername()); + assertTrue(passwordEncoder.matches(password, oAuthUser.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..ff60851 --- /dev/null +++ b/oauth-provider/src/test/resources/application.yml @@ -0,0 +1,14 @@ +# =============================== +# = 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 \ No newline at end of file From 99da3d9e0d5ffa31565b389b90e6f7083104b677 Mon Sep 17 00:00:00 2001 From: Valentin Zickner Date: Fri, 3 Jun 2016 18:48:16 +0200 Subject: [PATCH 04/17] Implement creation of new users. --- oauth-provider/oauth-provider.iml | 2 +- oauth-provider/pom.xml | 3 +-- .../de/helfenkannjeder/oauth/provider/domain/OAuthUser.java | 6 +++--- .../provider/domain/repository/OAuthUserRepository.java | 2 +- .../oauth/provider/rest/UserManagementController.java | 2 +- oauth-provider/src/main/resources/application.yml | 2 +- .../oauth/provider/rest/UserManagementControllerTest.java | 2 +- 7 files changed, 9 insertions(+), 10 deletions(-) diff --git a/oauth-provider/oauth-provider.iml b/oauth-provider/oauth-provider.iml index 3105275..098bd43 100644 --- a/oauth-provider/oauth-provider.iml +++ b/oauth-provider/oauth-provider.iml @@ -89,7 +89,7 @@ - + diff --git a/oauth-provider/pom.xml b/oauth-provider/pom.xml index c835b28..5881c90 100644 --- a/oauth-provider/pom.xml +++ b/oauth-provider/pom.xml @@ -10,7 +10,6 @@ 1.8 0.0.1-SNAPSHOT 4.12 - 5.1.38 1.4.182 @@ -43,7 +42,7 @@ mysql mysql-connector-java - ${mysql-connector-java.version} + runtime com.h2database 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 index 7e24f57..7594b0f 100644 --- 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 @@ -12,7 +12,7 @@ public class OAuthUser { @Id @GeneratedValue - private String id; + private Long id; private String username; private String password; @@ -24,11 +24,11 @@ public OAuthUser(String username, String password) { this.password = password; } - public String getId() { + public Long getId() { return id; } - public void setId(String id) { + public void setId(Long id) { this.id = id; } 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 index 9a6b7a2..bd6db24 100644 --- 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 @@ -6,5 +6,5 @@ /** * @author Valentin Zickner */ -public interface OAuthUserRepository extends CrudRepository { +public interface OAuthUserRepository extends CrudRepository { } 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 index 391f3db..e03cde4 100644 --- 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 @@ -36,7 +36,7 @@ public UserResponseDto create(@RequestBody UserRequestDto userRequestDto) { // TODO: Test if user already exists String password = passwordEncoder.encode(userRequestDto.getPassword()); OAuthUser user = oAuthUserRepository.save(new OAuthUser(userRequestDto.getUsername(), password)); - return new UserResponseDto(user.getId()); + return new UserResponseDto(String.valueOf(user.getId())); } @Override diff --git a/oauth-provider/src/main/resources/application.yml b/oauth-provider/src/main/resources/application.yml index 78861b2..fb0be4c 100644 --- a/oauth-provider/src/main/resources/application.yml +++ b/oauth-provider/src/main/resources/application.yml @@ -15,7 +15,7 @@ spring: jpa: show-sql: true hibernate: - ddl-auto: update + ddl-auto: create naming-strategy: org.hibernate.cfg.ImprovedNamingStrategy properties: hibernate: 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 index 8cbbaf0..2d1378c 100644 --- 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 @@ -74,7 +74,7 @@ public void create_withUsernameAndPassword_returnsNewUserId() throws Exception { UserResponseDto userResponseDto = objectMapper.readValue(result.getResponse().getContentAsString(), UserResponseDto.class); String userId = userResponseDto.getId(); assertNotNull(userId); - OAuthUser oAuthUser = oAuthUserRepository.findOne(userId); + OAuthUser oAuthUser = oAuthUserRepository.findOne(Long.valueOf(userId)); assertEquals(username, oAuthUser.getUsername()); assertTrue(passwordEncoder.matches(password, oAuthUser.getPassword())); } From d38093756926ffc592e5b47ef69ab6d4f563ef56 Mon Sep 17 00:00:00 2001 From: Valentin Zickner Date: Fri, 3 Jun 2016 23:02:06 +0200 Subject: [PATCH 05/17] Integrate OAuth2 user service to check user against database. --- .../OAuth2ProviderConfiguration.java | 18 +++++--- .../configuration/SecurityConfiguration.java | 17 ------- .../repository/OAuthUserRepository.java | 1 + .../oauth/provider/rest/UserController.java | 19 ++++++++ .../rest/UserManagementController.java | 7 --- .../service/OAuthUserDetailsService.java | 44 ++++++++++++++++++ .../src/main/resources/application.yml | 2 +- .../service/OAuthUserDetailsServiceTest.java | 45 +++++++++++++++++++ 8 files changed, 121 insertions(+), 32 deletions(-) delete mode 100644 oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/configuration/SecurityConfiguration.java create mode 100644 oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/rest/UserController.java create mode 100644 oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/service/OAuthUserDetailsService.java create mode 100644 oauth-provider/src/test/java/de/helfenkannjeder/oauth/provider/service/OAuthUserDetailsServiceTest.java 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 index b5db06e..6300857 100644 --- 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 @@ -1,9 +1,11 @@ package de.helfenkannjeder.oauth.provider.configuration; +import de.helfenkannjeder.oauth.provider.service.OAuthUserDetailsService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +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; @@ -19,16 +21,19 @@ @Configuration public class OAuth2ProviderConfiguration extends AuthorizationServerConfigurerAdapter { + public static final String COME2HELP_CLIENT_WEB = "come2help-web"; private TokenStore tokenStore = new InMemoryTokenStore(); @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { - clients.inMemory().withClient("come2help-web") + // @formatter:off + clients.inMemory().withClient(COME2HELP_CLIENT_WEB) .resourceIds("come2help") .authorizedGrantTypes("authorization_code") - .authorities("CLIENT") + .authorities(OAuthUserDetailsService.Authority.ROLE_USER.getAuthority()) .scopes("read", "write") .secret("secret"); + // @formatter:on } @Override @@ -42,12 +47,11 @@ public void configure(AuthorizationServerSecurityConfigurer security) throws Exc } @Autowired - public void configureGlobal(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception { + public void configureGlobal(AuthenticationManagerBuilder authenticationManagerBuilder, + UserDetailsService userDetailsService) throws Exception { authenticationManagerBuilder - .inMemoryAuthentication() - .withUser("user") - .password("password") - .roles("USER"); + .userDetailsService(userDetailsService) + .passwordEncoder(passwordEncoder()); } @Bean diff --git a/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/configuration/SecurityConfiguration.java b/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/configuration/SecurityConfiguration.java deleted file mode 100644 index 7a4a553..0000000 --- a/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/configuration/SecurityConfiguration.java +++ /dev/null @@ -1,17 +0,0 @@ -package de.helfenkannjeder.oauth.provider.configuration; - -import org.springframework.context.annotation.Configuration; -import org.springframework.security.config.annotation.web.builders.WebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; - -/** - * @author Valentin Zickner - */ -@Configuration -public class SecurityConfiguration extends WebSecurityConfigurerAdapter { - @Override - public void configure(WebSecurity web) throws Exception { - web.ignoring() - .antMatchers("/admin/**"); - } -} 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 index bd6db24..5afbef4 100644 --- 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 @@ -7,4 +7,5 @@ * @author Valentin Zickner */ public interface OAuthUserRepository extends CrudRepository { + OAuthUser findOneByUsername(String username); } 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..ae704c9 --- /dev/null +++ b/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/rest/UserController.java @@ -0,0 +1,19 @@ +package de.helfenkannjeder.oauth.provider.rest; + +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("/userInformation") + public Principal currentUser(Principal user) { + return user; + } + +} 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 index e03cde4..9bacffe 100644 --- 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 @@ -10,8 +10,6 @@ import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.web.bind.annotation.*; -import java.security.Principal; - /** * @author Valentin Zickner */ @@ -25,11 +23,6 @@ public class UserManagementController implements OAuthProviderUserManagementApi @Autowired private PasswordEncoder passwordEncoder; - @RequestMapping("/userInformation") - public Principal currentUser(Principal user) { - return user; - } - @Override @RequestMapping(value = CREATE, method = RequestMethod.POST) public UserResponseDto create(@RequestBody UserRequestDto userRequestDto) { diff --git a/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/service/OAuthUserDetailsService.java b/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/service/OAuthUserDetailsService.java new file mode 100644 index 0000000..69d7c34 --- /dev/null +++ b/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/service/OAuthUserDetailsService.java @@ -0,0 +1,44 @@ +package de.helfenkannjeder.oauth.provider.service; + +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.GrantedAuthority; +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.findOneByUsername(username); + if (user == null) { + return null; + } + return new User(user.getUsername(), user.getPassword(), Collections.singleton(Authority.ROLE_USER)); + } + + public enum Authority implements GrantedAuthority { + ROLE_USER; + + public String getAuthority() { + return name(); + } + } +} diff --git a/oauth-provider/src/main/resources/application.yml b/oauth-provider/src/main/resources/application.yml index fb0be4c..78861b2 100644 --- a/oauth-provider/src/main/resources/application.yml +++ b/oauth-provider/src/main/resources/application.yml @@ -15,7 +15,7 @@ spring: jpa: show-sql: true hibernate: - ddl-auto: create + ddl-auto: update naming-strategy: org.hibernate.cfg.ImprovedNamingStrategy properties: hibernate: 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..428a73d --- /dev/null +++ b/oauth-provider/src/test/java/de/helfenkannjeder/oauth/provider/service/OAuthUserDetailsServiceTest.java @@ -0,0 +1,45 @@ +package de.helfenkannjeder.oauth.provider.service; + +import de.helfenkannjeder.oauth.provider.domain.OAuthUser; +import de.helfenkannjeder.oauth.provider.domain.repository.OAuthUserRepository; +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.findOneByUsername("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 From 9c4322deb51fb5d7f7809b37b96fe5cee944c799 Mon Sep 17 00:00:00 2001 From: Valentin Zickner Date: Fri, 3 Jun 2016 23:44:14 +0200 Subject: [PATCH 06/17] Secure admin endpoint. --- .../OAuth2ProviderConfiguration.java | 15 ++++++++++++--- .../provider/rest/UserManagementController.java | 2 ++ .../provider/security/OAuthProviderAuthority.java | 14 ++++++++++++++ .../OAuthUserDetailsService.java | 12 ++---------- .../service/OAuthUserDetailsServiceTest.java | 1 + 5 files changed, 31 insertions(+), 13 deletions(-) create mode 100644 oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/security/OAuthProviderAuthority.java rename oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/{service => security}/OAuthUserDetailsService.java (79%) 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 index 6300857..7900c6b 100644 --- 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 @@ -1,6 +1,6 @@ package de.helfenkannjeder.oauth.provider.configuration; -import de.helfenkannjeder.oauth.provider.service.OAuthUserDetailsService; +import de.helfenkannjeder.oauth.provider.security.OAuthProviderAuthority; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -27,10 +27,19 @@ public class OAuth2ProviderConfiguration extends AuthorizationServerConfigurerAd @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { // @formatter:off - clients.inMemory().withClient(COME2HELP_CLIENT_WEB) + clients.inMemory() + .withClient("admin") + .secret("internal") + .resourceIds("come2help") + .authorizedGrantTypes("client_credentials") + .scopes("read") + .authorities(OAuthProviderAuthority.ROLE_ADMIN.getAuthority(), + OAuthProviderAuthority.ROLE_USER.getAuthority()) + .and() + .withClient(COME2HELP_CLIENT_WEB) .resourceIds("come2help") .authorizedGrantTypes("authorization_code") - .authorities(OAuthUserDetailsService.Authority.ROLE_USER.getAuthority()) + .authorities(OAuthProviderAuthority.ROLE_USER.getAuthority()) .scopes("read", "write") .secret("secret"); // @formatter:on 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 index 9bacffe..6cb6481 100644 --- 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 @@ -7,6 +7,7 @@ import de.helfenkannjeder.oauth.provider.domain.repository.OAuthUserRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; +import org.springframework.security.access.annotation.Secured; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.web.bind.annotation.*; @@ -15,6 +16,7 @@ */ @RestController @RequestMapping("/admin") +@Secured("ROLE_ADMIN") public class UserManagementController implements OAuthProviderUserManagementApi { @Autowired 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/service/OAuthUserDetailsService.java b/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/security/OAuthUserDetailsService.java similarity index 79% rename from oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/service/OAuthUserDetailsService.java rename to oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/security/OAuthUserDetailsService.java index 69d7c34..0830c7f 100644 --- a/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/service/OAuthUserDetailsService.java +++ b/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/security/OAuthUserDetailsService.java @@ -1,9 +1,8 @@ -package de.helfenkannjeder.oauth.provider.service; +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.GrantedAuthority; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; @@ -31,14 +30,7 @@ public UserDetails loadUserByUsername(String username) throws UsernameNotFoundEx if (user == null) { return null; } - return new User(user.getUsername(), user.getPassword(), Collections.singleton(Authority.ROLE_USER)); + return new User(user.getUsername(), user.getPassword(), Collections.singleton(OAuthProviderAuthority.ROLE_USER)); } - public enum Authority implements GrantedAuthority { - ROLE_USER; - - public String getAuthority() { - return name(); - } - } } 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 index 428a73d..a861d7e 100644 --- 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 @@ -2,6 +2,7 @@ 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; From e891b7c773a24d0f64fc4168a6ad6b8fbd26c6e6 Mon Sep 17 00:00:00 2001 From: Valentin Zickner Date: Sat, 4 Jun 2016 00:16:13 +0200 Subject: [PATCH 07/17] Code cleanup and dynamic configuration of clientIds and secrets. --- .../OAuth2ProviderConfiguration.java | 38 +++++++++++++------ .../rest/UserManagementController.java | 8 ++-- .../src/main/resources/application.yml | 10 ++++- .../rest/UserManagementControllerTest.java | 2 +- .../src/test/resources/application.yml | 10 ++++- 5 files changed, 49 insertions(+), 19 deletions(-) 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 index 7900c6b..280b084 100644 --- 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 @@ -2,6 +2,7 @@ import de.helfenkannjeder.oauth.provider.security.OAuthProviderAuthority; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; @@ -21,27 +22,40 @@ @Configuration public class OAuth2ProviderConfiguration extends AuthorizationServerConfigurerAdapter { - public static final String COME2HELP_CLIENT_WEB = "come2help-web"; + public static final String CLIENT_CREDENTIALS = "client_credentials"; + public static final String AUTHORIZATION_CODE = "authorization_code"; + public static final String SCOPE_DEFAULT = "default"; + 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; + @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { // @formatter:off clients.inMemory() - .withClient("admin") - .secret("internal") - .resourceIds("come2help") - .authorizedGrantTypes("client_credentials") - .scopes("read") + .withClient(adminClientId) + .secret(adminSecret) + .authorizedGrantTypes(CLIENT_CREDENTIALS) + .scopes(SCOPE_DEFAULT) .authorities(OAuthProviderAuthority.ROLE_ADMIN.getAuthority(), OAuthProviderAuthority.ROLE_USER.getAuthority()) .and() - .withClient(COME2HELP_CLIENT_WEB) - .resourceIds("come2help") - .authorizedGrantTypes("authorization_code") - .authorities(OAuthProviderAuthority.ROLE_USER.getAuthority()) - .scopes("read", "write") - .secret("secret"); + .withClient(come2helpClientId) + .secret(come2helpClientSecret) + .authorizedGrantTypes(AUTHORIZATION_CODE) + .scopes(SCOPE_DEFAULT) + .authorities(OAuthProviderAuthority.ROLE_USER.getAuthority()); // @formatter:on } 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 index 6cb6481..3465566 100644 --- 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 @@ -37,14 +37,14 @@ public UserResponseDto create(@RequestBody UserRequestDto userRequestDto) { @Override @RequestMapping(value = UPDATE, method = RequestMethod.PUT) @ResponseStatus(HttpStatus.NO_CONTENT) - public void update(@RequestParam("id") String id, UserRequestDto userRequestDto) { - + public void update(@PathVariable("id") String id, UserRequestDto userRequestDto) { + throw new RuntimeException("Not yet implemented"); } @Override @RequestMapping(value = DELETE, method = RequestMethod.DELETE) @ResponseStatus(HttpStatus.NO_CONTENT) - public void delete(@RequestParam("id") String id) { - + public void delete(@PathVariable("id") String id) { + throw new RuntimeException("Not yet implemented"); } } \ No newline at end of file diff --git a/oauth-provider/src/main/resources/application.yml b/oauth-provider/src/main/resources/application.yml index 78861b2..1a35644 100644 --- a/oauth-provider/src/main/resources/application.yml +++ b/oauth-provider/src/main/resources/application.yml @@ -19,4 +19,12 @@ spring: naming-strategy: org.hibernate.cfg.ImprovedNamingStrategy properties: hibernate: - dialect: org.hibernate.dialect.MySQL5Dialect \ No newline at end of file + 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/rest/UserManagementControllerTest.java b/oauth-provider/src/test/java/de/helfenkannjeder/oauth/provider/rest/UserManagementControllerTest.java index 2d1378c..38d47b1 100644 --- 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 @@ -31,7 +31,7 @@ * @author Valentin Zickner */ @RunWith(SpringJUnit4ClassRunner.class) -@WebIntegrationTest +@WebIntegrationTest(randomPort = true) @SpringApplicationConfiguration(classes = OAuth2ProviderApplication.class) public class UserManagementControllerTest { diff --git a/oauth-provider/src/test/resources/application.yml b/oauth-provider/src/test/resources/application.yml index ff60851..c10ccaa 100644 --- a/oauth-provider/src/test/resources/application.yml +++ b/oauth-provider/src/test/resources/application.yml @@ -11,4 +11,12 @@ spring: jpa: properties: hibernate: - dialect: org.hibernate.dialect.H2Dialect \ No newline at end of file + 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 From e9fec6b002c543e63892494b03e03d85219920fd Mon Sep 17 00:00:00 2001 From: Valentin Zickner Date: Sat, 4 Jun 2016 02:34:07 +0200 Subject: [PATCH 08/17] Add test security to also verify roles. --- oauth-provider/oauth-provider.iml | 1 + oauth-provider/pom.xml | 5 + .../OAuth2ProviderConfiguration.java | 1 + .../rest/UserManagementControllerTest.java | 125 +++++++++++++++++- 4 files changed, 128 insertions(+), 4 deletions(-) diff --git a/oauth-provider/oauth-provider.iml b/oauth-provider/oauth-provider.iml index 098bd43..a75dc50 100644 --- a/oauth-provider/oauth-provider.iml +++ b/oauth-provider/oauth-provider.iml @@ -97,6 +97,7 @@ + diff --git a/oauth-provider/pom.xml b/oauth-provider/pom.xml index 5881c90..3018777 100644 --- a/oauth-provider/pom.xml +++ b/oauth-provider/pom.xml @@ -55,6 +55,11 @@ spring-boot-starter-test test + + org.springframework.security + spring-security-test + test + junit junit 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 index 280b084..ec9d08c 100644 --- 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 @@ -24,6 +24,7 @@ public class OAuth2ProviderConfiguration extends AuthorizationServerConfigurerAd 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"; private TokenStore tokenStore = new InMemoryTokenStore(); 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 index 38d47b1..085c9bc 100644 --- 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 @@ -4,24 +4,48 @@ import de.helfenkannjeder.oauth.provider.api.dto.UserRequestDto; import de.helfenkannjeder.oauth.provider.api.dto.UserResponseDto; import de.helfenkannjeder.oauth.provider.configuration.OAuth2ProviderApplication; +import de.helfenkannjeder.oauth.provider.configuration.OAuth2ProviderConfiguration; import de.helfenkannjeder.oauth.provider.domain.OAuthUser; import de.helfenkannjeder.oauth.provider.domain.repository.OAuthUserRepository; +import de.helfenkannjeder.oauth.provider.security.OAuthProviderAuthority; import org.codehaus.jackson.map.ObjectMapper; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; 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.http.MediaType; +import org.springframework.security.core.Authentication; import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.oauth2.provider.ClientDetails; +import org.springframework.security.oauth2.provider.OAuth2Authentication; +import org.springframework.security.oauth2.provider.TokenRequest; +import org.springframework.security.oauth2.provider.client.BaseClientDetails; +import org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener; +import org.springframework.test.context.TestExecutionListeners; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.support.DependencyInjectionTestExecutionListener; +import org.springframework.test.context.support.DirtiesContextTestExecutionListener; +import org.springframework.test.context.transaction.TransactionalTestExecutionListener; +import org.springframework.test.context.web.ServletTestExecutionListener; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; +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 static java.util.Collections.singleton; import static junit.framework.TestCase.assertNotNull; +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.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; @@ -33,9 +57,16 @@ @RunWith(SpringJUnit4ClassRunner.class) @WebIntegrationTest(randomPort = true) @SpringApplicationConfiguration(classes = OAuth2ProviderApplication.class) +@TestExecutionListeners(listeners = {ServletTestExecutionListener.class, + DependencyInjectionTestExecutionListener.class, + DirtiesContextTestExecutionListener.class, + TransactionalTestExecutionListener.class, + WithSecurityContextTestExecutionListener.class}) public class UserManagementControllerTest { public static final String RESOURCE_PREFIX = "/admin"; + public static final String DEFAULT_USER = "my-default-user"; + public static final String DEFAULT_PASSWORD = "my-default-password"; @Autowired private WebApplicationContext webApplicationContext; @@ -49,19 +80,82 @@ public class UserManagementControllerTest { @Autowired private PasswordEncoder passwordEncoder; + @Autowired + private Filter springSecurityFilterChain; + + private UserRequestDto userRequestDto; + + private Authentication createAuthentication(String grantType, OAuthProviderAuthority... authorities) { + + StringBuilder authoritiesBuilder = new StringBuilder(); + for (OAuthProviderAuthority authority : authorities) { + authoritiesBuilder.append(",").append(authority); + } + ClientDetails client = new BaseClientDetails("oauth-provider-admin", null, OAuth2ProviderConfiguration.SCOPE_DEFAULT, grantType, authoritiesBuilder.substring(1)); + return new OAuth2Authentication(new TokenRequest(new HashMap<>(), "oauth-provider-admin", singleton(OAuth2ProviderConfiguration.SCOPE_DEFAULT), grantType).createOAuth2Request(client), null); + } + @Before public void setUp() throws Exception { - this.mockMvc = MockMvcBuilders.webAppContextSetup(this.webApplicationContext).build(); + this.mockMvc = MockMvcBuilders + .webAppContextSetup(this.webApplicationContext) + .addFilters(springSecurityFilterChain) + .build(); + userRequestDto = new UserRequestDto("my-user", "my-password"); + + oAuthUserRepository.save(new OAuthUser(DEFAULT_USER, DEFAULT_PASSWORD)); } + private String getAccessToken(String clientId, String secret) throws Exception { + return getAccessToken(clientId, secret, null, null); + } + + /* + * Original from https://github.com/royclarkson/spring-rest-service-oauth/blob/master/src/test/java/hello/GreetingControllerTest.java + */ + private String getAccessToken(String clientId, String secret, String username, String password) throws Exception { + String authorization = "Basic " + new String(Base64Utils.encode((clientId + ":" + secret).getBytes())); + + MockHttpServletRequestBuilder post = post("/oauth/token"); + String grantType = OAuth2ProviderConfiguration.CLIENT_CREDENTIALS; + if (username != null && password != null) { + post = post.param("username", username).param("password", password); + grantType = OAuth2ProviderConfiguration.PASSWORD; + } + + // @formatter:off + String content = mockMvc.perform(post + .header("Authorization", authorization) + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .param("grant_type", grantType) + .param("scope", OAuth2ProviderConfiguration.SCOPE_DEFAULT) + .param("client_id", clientId) + .param("client_secret", secret)) + .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 + + HashMap result = objectMapper.readValue(content, HashMap.class); + + return "Bearer " + result.get("access_token"); + } + + @Test - public void create_withUsernameAndPassword_returnsNewUserId() throws Exception { + public void create_withAdminAndUsernameAndPassword_returnsNewUserId() throws Exception { + // Arrange String username = "my-user"; String password = "my-password"; - UserRequestDto user = new UserRequestDto(username, password); + + // Act MvcResult result = this.mockMvc.perform(post(RESOURCE_PREFIX + OAuthProviderUserManagementApi.CREATE) + .header("Authorization", getAccessToken("oauth-provider-admin", "default-secret")) .contentType(MediaType.APPLICATION_JSON_UTF8) - .content(objectMapper.writeValueAsString(user)) + .content(objectMapper.writeValueAsString(userRequestDto)) .accept(MediaType.APPLICATION_JSON_UTF8) ) .andExpect(status().isOk()) @@ -70,6 +164,7 @@ public void create_withUsernameAndPassword_returnsNewUserId() throws Exception { .andExpect(jsonPath("$.id").isNotEmpty()) .andReturn(); + // Assert assertNotNull(result); UserResponseDto userResponseDto = objectMapper.readValue(result.getResponse().getContentAsString(), UserResponseDto.class); String userId = userResponseDto.getId(); @@ -78,4 +173,26 @@ public void create_withUsernameAndPassword_returnsNewUserId() throws Exception { assertEquals(username, oAuthUser.getUsername()); assertTrue(passwordEncoder.matches(password, oAuthUser.getPassword())); } + + @Test + @Ignore("Endpoint for password is still missing") + public void create_withNotAdmin_returns403() throws Exception { + // Act + Assert + this.mockMvc.perform(post(RESOURCE_PREFIX + OAuthProviderUserManagementApi.CREATE) + .header("Authorization", getAccessToken("come2help-web", "secret", DEFAULT_USER, DEFAULT_PASSWORD)) + .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()); + } } \ No newline at end of file From c3deb71a3605afd6b861e5fe08dac75a90d2954b Mon Sep 17 00:00:00 2001 From: Valentin Zickner Date: Wed, 8 Jun 2016 18:18:24 +0200 Subject: [PATCH 09/17] Add MockMvcOAuthLogin and fix access rights of endpoints. --- .../OAuth2ProviderApplication.java | 2 - .../OAuth2ProviderConfiguration.java | 5 +- .../ResourceServerConfiguration.java | 20 +++ .../rest/UserManagementController.java | 2 - .../oauth/provider/MockMvcOAuthLogin.java | 132 ++++++++++++++++++ .../rest/UserManagementControllerTest.java | 69 ++------- 6 files changed, 164 insertions(+), 66 deletions(-) create mode 100644 oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/configuration/ResourceServerConfiguration.java create mode 100644 oauth-provider/src/test/java/de/helfenkannjeder/oauth/provider/MockMvcOAuthLogin.java 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 index 81f927c..ec7c98a 100644 --- 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 @@ -6,13 +6,11 @@ import org.springframework.context.annotation.ComponentScan; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; -import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; /** * @author Valentin Zickner */ @SpringBootApplication -@EnableResourceServer @EnableAuthorizationServer @EntityScan(basePackages = "de.helfenkannjeder.oauth.provider.domain") @EnableJpaRepositories("de.helfenkannjeder.oauth.provider.domain.repository") 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 index ec9d08c..078c022 100644 --- 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 @@ -26,6 +26,7 @@ public class OAuth2ProviderConfiguration extends AuthorizationServerConfigurerAd 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(); @@ -47,14 +48,14 @@ public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() .withClient(adminClientId) .secret(adminSecret) - .authorizedGrantTypes(CLIENT_CREDENTIALS) + .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) + .authorizedGrantTypes(AUTHORIZATION_CODE, REFRESH_TOKEN) .scopes(SCOPE_DEFAULT) .authorities(OAuthProviderAuthority.ROLE_USER.getAuthority()); // @formatter:on 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..13dd3bb --- /dev/null +++ b/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/configuration/ResourceServerConfiguration.java @@ -0,0 +1,20 @@ +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 { + http.authorizeRequests() + .antMatchers("/admin/**").hasAuthority("ROLE_ADMIN") + .antMatchers("/**").authenticated(); + } +} 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 index 3465566..b6bba50 100644 --- 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 @@ -7,7 +7,6 @@ import de.helfenkannjeder.oauth.provider.domain.repository.OAuthUserRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; -import org.springframework.security.access.annotation.Secured; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.web.bind.annotation.*; @@ -16,7 +15,6 @@ */ @RestController @RequestMapping("/admin") -@Secured("ROLE_ADMIN") public class UserManagementController implements OAuthProviderUserManagementApi { @Autowired 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..b60f2f6 --- /dev/null +++ b/oauth-provider/src/test/java/de/helfenkannjeder/oauth/provider/MockMvcOAuthLogin.java @@ -0,0 +1,132 @@ +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.stereotype.Service; +import org.springframework.test.web.servlet.MockMvc; +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 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 String getAccessTokenWithClientSecret(String clientId, String secret) throws Exception { + return getAccessToken(post(ENDPOINT_OAUTH_TOKEN), OAuth2ProviderConfiguration.CLIENT_CREDENTIALS, clientId, secret); + } + + public String getAccessTokenWithAuthorizationCode(String clientId, String secret, String username, String password) throws Exception { + MockHttpSession mockSession = new MockHttpSession(webApplicationContext.getServletContext(), UUID.randomUUID().toString()); + + mockMvc.perform( + get(ENDPOINT_OAUTH_AUTHORIZATION) + .session(mockSession) + .header("Authorization", createBase64Auth(username, password)) + .param("response_type", "code") + .param("redirect_uri", "/") + .param("client_id", clientId) + ) + .andExpect(status().isOk()) + .andExpect(forwardedUrl(ENDPOINT_CONFIRM_ACCESS)) + .andReturn().getResponse(); + + MockHttpServletResponse response = mockMvc.perform( + post(ENDPOINT_OAUTH_AUTHORIZATION) + .session(mockSession) + .header("Authorization", createBase64Auth(username, password)) + .param("scope.default", "true") + .param("user_oauth_approval", "true") + .param("authorize", "Authorize") + ) + .andExpect(status().is3xxRedirection()) + .andReturn().getResponse(); + + String content = response.getRedirectedUrl(); + String code = content.split("=")[1]; + return getAccessTokenWithAuthorizationCode(clientId, secret, code); + } + + private String 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 String 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); + } + + /* + * Original from https://github.com/royclarkson/spring-rest-service-oauth/blob/master/src/test/java/hello/GreetingControllerTest.java + */ + private String 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) + .param("client_id", clientId) + .param("client_secret", secret)) + .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 "Bearer " + result.get("access_token"); + } + + private String createBase64Auth(String username, String password) { + return "Basic " + new String(Base64Utils.encode((username + ":" + password).getBytes())); + } + + +} 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 index 085c9bc..1a7811b 100644 --- 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 @@ -8,9 +8,9 @@ import de.helfenkannjeder.oauth.provider.domain.OAuthUser; import de.helfenkannjeder.oauth.provider.domain.repository.OAuthUserRepository; import de.helfenkannjeder.oauth.provider.security.OAuthProviderAuthority; +import de.helfenkannjeder.oauth.provider.MockMvcOAuthLogin; import org.codehaus.jackson.map.ObjectMapper; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; @@ -23,18 +23,11 @@ import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.security.oauth2.provider.TokenRequest; import org.springframework.security.oauth2.provider.client.BaseClientDetails; -import org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener; -import org.springframework.test.context.TestExecutionListeners; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import org.springframework.test.context.support.DependencyInjectionTestExecutionListener; -import org.springframework.test.context.support.DirtiesContextTestExecutionListener; -import org.springframework.test.context.transaction.TransactionalTestExecutionListener; -import org.springframework.test.context.web.ServletTestExecutionListener; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; -import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import org.springframework.util.Base64Utils; +import org.springframework.transaction.annotation.Transactional; import org.springframework.web.context.WebApplicationContext; import javax.servlet.Filter; @@ -42,10 +35,6 @@ import static java.util.Collections.singleton; import static junit.framework.TestCase.assertNotNull; -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.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; @@ -57,11 +46,7 @@ @RunWith(SpringJUnit4ClassRunner.class) @WebIntegrationTest(randomPort = true) @SpringApplicationConfiguration(classes = OAuth2ProviderApplication.class) -@TestExecutionListeners(listeners = {ServletTestExecutionListener.class, - DependencyInjectionTestExecutionListener.class, - DirtiesContextTestExecutionListener.class, - TransactionalTestExecutionListener.class, - WithSecurityContextTestExecutionListener.class}) +@Transactional public class UserManagementControllerTest { public static final String RESOURCE_PREFIX = "/admin"; @@ -83,6 +68,9 @@ public class UserManagementControllerTest { @Autowired private Filter springSecurityFilterChain; + @Autowired + private MockMvcOAuthLogin mockMvcOAuthLogin; + private UserRequestDto userRequestDto; private Authentication createAuthentication(String grantType, OAuthProviderAuthority... authorities) { @@ -103,45 +91,7 @@ public void setUp() throws Exception { .build(); userRequestDto = new UserRequestDto("my-user", "my-password"); - oAuthUserRepository.save(new OAuthUser(DEFAULT_USER, DEFAULT_PASSWORD)); - } - - private String getAccessToken(String clientId, String secret) throws Exception { - return getAccessToken(clientId, secret, null, null); - } - - /* - * Original from https://github.com/royclarkson/spring-rest-service-oauth/blob/master/src/test/java/hello/GreetingControllerTest.java - */ - private String getAccessToken(String clientId, String secret, String username, String password) throws Exception { - String authorization = "Basic " + new String(Base64Utils.encode((clientId + ":" + secret).getBytes())); - - MockHttpServletRequestBuilder post = post("/oauth/token"); - String grantType = OAuth2ProviderConfiguration.CLIENT_CREDENTIALS; - if (username != null && password != null) { - post = post.param("username", username).param("password", password); - grantType = OAuth2ProviderConfiguration.PASSWORD; - } - - // @formatter:off - String content = mockMvc.perform(post - .header("Authorization", authorization) - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .param("grant_type", grantType) - .param("scope", OAuth2ProviderConfiguration.SCOPE_DEFAULT) - .param("client_id", clientId) - .param("client_secret", secret)) - .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 - - HashMap result = objectMapper.readValue(content, HashMap.class); - - return "Bearer " + result.get("access_token"); + oAuthUserRepository.save(new OAuthUser(DEFAULT_USER, passwordEncoder.encode(DEFAULT_PASSWORD))); } @@ -153,7 +103,7 @@ public void create_withAdminAndUsernameAndPassword_returnsNewUserId() throws Exc // Act MvcResult result = this.mockMvc.perform(post(RESOURCE_PREFIX + OAuthProviderUserManagementApi.CREATE) - .header("Authorization", getAccessToken("oauth-provider-admin", "default-secret")) + .header("Authorization", mockMvcOAuthLogin.getAccessTokenWithClientSecret("oauth-provider-admin", "default-secret")) .contentType(MediaType.APPLICATION_JSON_UTF8) .content(objectMapper.writeValueAsString(userRequestDto)) .accept(MediaType.APPLICATION_JSON_UTF8) @@ -175,11 +125,10 @@ public void create_withAdminAndUsernameAndPassword_returnsNewUserId() throws Exc } @Test - @Ignore("Endpoint for password is still missing") public void create_withNotAdmin_returns403() throws Exception { // Act + Assert this.mockMvc.perform(post(RESOURCE_PREFIX + OAuthProviderUserManagementApi.CREATE) - .header("Authorization", getAccessToken("come2help-web", "secret", DEFAULT_USER, DEFAULT_PASSWORD)) + .header("Authorization", mockMvcOAuthLogin.getAccessTokenWithAuthorizationCode("come2help-web", "secret", DEFAULT_USER, DEFAULT_PASSWORD)) .contentType(MediaType.APPLICATION_JSON_UTF8) .content(objectMapper.writeValueAsString(userRequestDto)) .accept(MediaType.APPLICATION_JSON_UTF8) From 52a9fc769335ddc4fb6aab232add86b23c72e8db Mon Sep 17 00:00:00 2001 From: Valentin Zickner Date: Wed, 8 Jun 2016 18:30:06 +0200 Subject: [PATCH 10/17] Add check for duplicate username. --- .../repository/OAuthUserRepository.java | 2 +- .../UsernameAlreadyExistsException.java | 11 +++++ .../rest/UserManagementController.java | 6 ++- .../security/OAuthUserDetailsService.java | 2 +- .../rest/UserManagementControllerTest.java | 48 +++++++++++++++++-- .../service/OAuthUserDetailsServiceTest.java | 2 +- 6 files changed, 64 insertions(+), 7 deletions(-) create mode 100644 oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/exception/UsernameAlreadyExistsException.java 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 index 5afbef4..344ef9d 100644 --- 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 @@ -7,5 +7,5 @@ * @author Valentin Zickner */ public interface OAuthUserRepository extends CrudRepository { - OAuthUser findOneByUsername(String username); + 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/UserManagementController.java b/oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/rest/UserManagementController.java index b6bba50..2632868 100644 --- 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 @@ -5,6 +5,7 @@ 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; @@ -26,7 +27,10 @@ public class UserManagementController implements OAuthProviderUserManagementApi @Override @RequestMapping(value = CREATE, method = RequestMethod.POST) public UserResponseDto create(@RequestBody UserRequestDto userRequestDto) { - // TODO: Test if user already exists + if (oAuthUserRepository.findOneByUsernameIgnoreCase(userRequestDto.getUsername()) != null) { + throw new UsernameAlreadyExistsException(); + } + String password = passwordEncoder.encode(userRequestDto.getPassword()); OAuthUser user = oAuthUserRepository.save(new OAuthUser(userRequestDto.getUsername(), password)); return new UserResponseDto(String.valueOf(user.getId())); 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 index 0830c7f..5946e40 100644 --- 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 @@ -26,7 +26,7 @@ public OAuthUserDetailsService(OAuthUserRepository oAuthUserRepository) { @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { - OAuthUser user = oAuthUserRepository.findOneByUsername(username); + OAuthUser user = oAuthUserRepository.findOneByUsernameIgnoreCase(username); if (user == null) { return null; } 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 index 1a7811b..b6cc2b2 100644 --- 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 @@ -1,5 +1,6 @@ package de.helfenkannjeder.oauth.provider.rest; +import de.helfenkannjeder.oauth.provider.MockMvcOAuthLogin; import de.helfenkannjeder.oauth.provider.api.OAuthProviderUserManagementApi; import de.helfenkannjeder.oauth.provider.api.dto.UserRequestDto; import de.helfenkannjeder.oauth.provider.api.dto.UserResponseDto; @@ -8,7 +9,6 @@ import de.helfenkannjeder.oauth.provider.domain.OAuthUser; import de.helfenkannjeder.oauth.provider.domain.repository.OAuthUserRepository; import de.helfenkannjeder.oauth.provider.security.OAuthProviderAuthority; -import de.helfenkannjeder.oauth.provider.MockMvcOAuthLogin; import org.codehaus.jackson.map.ObjectMapper; import org.junit.Before; import org.junit.Test; @@ -103,7 +103,7 @@ public void create_withAdminAndUsernameAndPassword_returnsNewUserId() throws Exc // Act MvcResult result = this.mockMvc.perform(post(RESOURCE_PREFIX + OAuthProviderUserManagementApi.CREATE) - .header("Authorization", mockMvcOAuthLogin.getAccessTokenWithClientSecret("oauth-provider-admin", "default-secret")) + .header("Authorization", getAuthorizationAdmin()) .contentType(MediaType.APPLICATION_JSON_UTF8) .content(objectMapper.writeValueAsString(userRequestDto)) .accept(MediaType.APPLICATION_JSON_UTF8) @@ -124,11 +124,45 @@ public void create_withAdminAndUsernameAndPassword_returnsNewUserId() throws Exc assertTrue(passwordEncoder.matches(password, oAuthUser.getPassword())); } + @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", mockMvcOAuthLogin.getAccessTokenWithAuthorizationCode("come2help-web", "secret", DEFAULT_USER, DEFAULT_PASSWORD)) + .header("Authorization", getAuthorizationDefaultUser()) .contentType(MediaType.APPLICATION_JSON_UTF8) .content(objectMapper.writeValueAsString(userRequestDto)) .accept(MediaType.APPLICATION_JSON_UTF8) @@ -144,4 +178,12 @@ public void create_withNoAuthentication_returns401() throws Exception { .accept(MediaType.APPLICATION_JSON_UTF8) ).andExpect(status().isUnauthorized()); } + + private String getAuthorizationAdmin() throws Exception { + return mockMvcOAuthLogin.getAccessTokenWithClientSecret("oauth-provider-admin", "default-secret"); + } + + private String getAuthorizationDefaultUser() throws Exception { + return mockMvcOAuthLogin.getAccessTokenWithAuthorizationCode("come2help-web", "secret", DEFAULT_USER, DEFAULT_PASSWORD); + } } \ 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 index a861d7e..ef7bc65 100644 --- 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 @@ -27,7 +27,7 @@ public class OAuthUserDetailsServiceTest { @Before public void setUp() throws Exception { - when(oAuthUserRepository.findOneByUsername("my-user")).thenReturn(new OAuthUser("my-user", "$2a$10$mXEdVKm16/vj/JyE.MgQ..UBa0p4rF1JYeGvLzvOJykact6UPVRx.")); + when(oAuthUserRepository.findOneByUsernameIgnoreCase("my-user")).thenReturn(new OAuthUser("my-user", "$2a$10$mXEdVKm16/vj/JyE.MgQ..UBa0p4rF1JYeGvLzvOJykact6UPVRx.")); oAuthUserDetailsService = new OAuthUserDetailsService(oAuthUserRepository); } From 14f7453be4aed0ab4528a022845226958a964b42 Mon Sep 17 00:00:00 2001 From: Valentin Zickner Date: Wed, 8 Jun 2016 18:57:40 +0200 Subject: [PATCH 11/17] Refactor test methods. --- .../oauth/provider/MockMvcOAuthLogin.java | 4 +- .../rest/AbstractOAuthControllerTest.java | 63 +++++++++++++++++ .../rest/UserManagementControllerTest.java | 70 +------------------ 3 files changed, 65 insertions(+), 72 deletions(-) create mode 100644 oauth-provider/src/test/java/de/helfenkannjeder/oauth/provider/rest/AbstractOAuthControllerTest.java 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 index b60f2f6..81eb04f 100644 --- a/oauth-provider/src/test/java/de/helfenkannjeder/oauth/provider/MockMvcOAuthLogin.java +++ b/oauth-provider/src/test/java/de/helfenkannjeder/oauth/provider/MockMvcOAuthLogin.java @@ -107,9 +107,7 @@ private String getAccessToken(MockHttpServletRequestBuilder post, String grantTy .header("Authorization", createBase64Auth(clientId, secret)) .contentType(MediaType.APPLICATION_FORM_URLENCODED) .param("grant_type", grantType) - .param("scope", OAuth2ProviderConfiguration.SCOPE_DEFAULT) - .param("client_id", clientId) - .param("client_secret", secret)) + .param("scope", OAuth2ProviderConfiguration.SCOPE_DEFAULT)) .andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8)) .andExpect(jsonPath("$.access_token", is(notNullValue()))) 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..05bde44 --- /dev/null +++ b/oauth-provider/src/test/java/de/helfenkannjeder/oauth/provider/rest/AbstractOAuthControllerTest.java @@ -0,0 +1,63 @@ +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 + private 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"); + } + + protected String getAuthorizationDefaultUser() throws Exception { + return mockMvcOAuthLogin.getAccessTokenWithAuthorizationCode("come2help-web", "secret", AbstractOAuthControllerTest.DEFAULT_USER, AbstractOAuthControllerTest.DEFAULT_PASSWORD); + } +} 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 index b6cc2b2..07f5291 100644 --- 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 @@ -1,39 +1,16 @@ package de.helfenkannjeder.oauth.provider.rest; -import de.helfenkannjeder.oauth.provider.MockMvcOAuthLogin; 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.configuration.OAuth2ProviderApplication; -import de.helfenkannjeder.oauth.provider.configuration.OAuth2ProviderConfiguration; import de.helfenkannjeder.oauth.provider.domain.OAuthUser; -import de.helfenkannjeder.oauth.provider.domain.repository.OAuthUserRepository; -import de.helfenkannjeder.oauth.provider.security.OAuthProviderAuthority; import org.codehaus.jackson.map.ObjectMapper; import org.junit.Before; import org.junit.Test; -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.http.MediaType; -import org.springframework.security.core.Authentication; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.security.oauth2.provider.ClientDetails; -import org.springframework.security.oauth2.provider.OAuth2Authentication; -import org.springframework.security.oauth2.provider.TokenRequest; -import org.springframework.security.oauth2.provider.client.BaseClientDetails; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.context.WebApplicationContext; -import javax.servlet.Filter; -import java.util.HashMap; - -import static java.util.Collections.singleton; import static junit.framework.TestCase.assertNotNull; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -43,58 +20,20 @@ /** * @author Valentin Zickner */ -@RunWith(SpringJUnit4ClassRunner.class) -@WebIntegrationTest(randomPort = true) -@SpringApplicationConfiguration(classes = OAuth2ProviderApplication.class) @Transactional -public class UserManagementControllerTest { +public class UserManagementControllerTest extends AbstractOAuthControllerTest { public static final String RESOURCE_PREFIX = "/admin"; - public static final String DEFAULT_USER = "my-default-user"; - public static final String DEFAULT_PASSWORD = "my-default-password"; - - @Autowired - private WebApplicationContext webApplicationContext; - private MockMvc mockMvc; private ObjectMapper objectMapper = new ObjectMapper(); - @Autowired - private OAuthUserRepository oAuthUserRepository; - - @Autowired - private PasswordEncoder passwordEncoder; - - @Autowired - private Filter springSecurityFilterChain; - - @Autowired - private MockMvcOAuthLogin mockMvcOAuthLogin; - private UserRequestDto userRequestDto; - private Authentication createAuthentication(String grantType, OAuthProviderAuthority... authorities) { - - StringBuilder authoritiesBuilder = new StringBuilder(); - for (OAuthProviderAuthority authority : authorities) { - authoritiesBuilder.append(",").append(authority); - } - ClientDetails client = new BaseClientDetails("oauth-provider-admin", null, OAuth2ProviderConfiguration.SCOPE_DEFAULT, grantType, authoritiesBuilder.substring(1)); - return new OAuth2Authentication(new TokenRequest(new HashMap<>(), "oauth-provider-admin", singleton(OAuth2ProviderConfiguration.SCOPE_DEFAULT), grantType).createOAuth2Request(client), null); - } - @Before public void setUp() throws Exception { - this.mockMvc = MockMvcBuilders - .webAppContextSetup(this.webApplicationContext) - .addFilters(springSecurityFilterChain) - .build(); userRequestDto = new UserRequestDto("my-user", "my-password"); - - oAuthUserRepository.save(new OAuthUser(DEFAULT_USER, passwordEncoder.encode(DEFAULT_PASSWORD))); } - @Test public void create_withAdminAndUsernameAndPassword_returnsNewUserId() throws Exception { // Arrange @@ -179,11 +118,4 @@ public void create_withNoAuthentication_returns401() throws Exception { ).andExpect(status().isUnauthorized()); } - private String getAuthorizationAdmin() throws Exception { - return mockMvcOAuthLogin.getAccessTokenWithClientSecret("oauth-provider-admin", "default-secret"); - } - - private String getAuthorizationDefaultUser() throws Exception { - return mockMvcOAuthLogin.getAccessTokenWithAuthorizationCode("come2help-web", "secret", DEFAULT_USER, DEFAULT_PASSWORD); - } } \ No newline at end of file From 88e9cb1559221b36208cc50e2d719b137bd67eeb Mon Sep 17 00:00:00 2001 From: Valentin Zickner Date: Wed, 8 Jun 2016 19:04:29 +0200 Subject: [PATCH 12/17] Implement user information endpoint with username. --- .../oauth/provider/rest/UserController.java | 7 ++-- .../provider/rest/UserControllerTest.java | 41 +++++++++++++++++++ 2 files changed, 45 insertions(+), 3 deletions(-) create mode 100644 oauth-provider/src/test/java/de/helfenkannjeder/oauth/provider/rest/UserControllerTest.java 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 index ae704c9..93836b5 100644 --- 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 @@ -1,5 +1,6 @@ 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; @@ -11,9 +12,9 @@ @RestController public class UserController { - @RequestMapping("/userInformation") - public Principal currentUser(Principal user) { - return user; + @RequestMapping("/user/information") + public OAuthUser currentUser(Principal user) { + return new OAuthUser(user.getName(), null); } } 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..f8c59ec --- /dev/null +++ b/oauth-provider/src/test/java/de/helfenkannjeder/oauth/provider/rest/UserControllerTest.java @@ -0,0 +1,41 @@ +package de.helfenkannjeder.oauth.provider.rest; + +import org.junit.Test; +import org.springframework.http.MediaType; +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 +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_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 From efff5ac0f825af37e7173743a2ba2e78be6357ba Mon Sep 17 00:00:00 2001 From: Valentin Zickner Date: Wed, 8 Jun 2016 19:06:39 +0200 Subject: [PATCH 13/17] Fix dirty context issue during test execution. --- .../helfenkannjeder/oauth/provider/rest/UserControllerTest.java | 2 ++ .../oauth/provider/rest/UserManagementControllerTest.java | 2 ++ 2 files changed, 4 insertions(+) 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 index f8c59ec..aa27095 100644 --- 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 @@ -2,6 +2,7 @@ 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; @@ -11,6 +12,7 @@ * @author Valentin Zickner */ @Transactional +@DirtiesContext public class UserControllerTest extends AbstractOAuthControllerTest { @Test 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 index 07f5291..7b037ea 100644 --- 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 @@ -8,6 +8,7 @@ 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; @@ -21,6 +22,7 @@ * @author Valentin Zickner */ @Transactional +@DirtiesContext public class UserManagementControllerTest extends AbstractOAuthControllerTest { public static final String RESOURCE_PREFIX = "/admin"; From 6986d21aa902b6e66edc35d43cf4b0b2c74723d5 Mon Sep 17 00:00:00 2001 From: Valentin Zickner Date: Wed, 8 Jun 2016 19:44:06 +0200 Subject: [PATCH 14/17] Implement update of user and add validation of user objects. --- oauth-provider-api/oauth-provider-api.iml | 4 + oauth-provider-api/pom.xml | 5 + .../provider/api/dto/UserRequestDto.java | 9 ++ oauth-provider/oauth-provider.iml | 8 +- oauth-provider/pom.xml | 4 + .../rest/UserManagementController.java | 26 +++- .../rest/UserManagementControllerTest.java | 119 +++++++++++++++++- pom.xml | 1 + 8 files changed, 160 insertions(+), 16 deletions(-) diff --git a/oauth-provider-api/oauth-provider-api.iml b/oauth-provider-api/oauth-provider-api.iml index 85a0502..b82857a 100644 --- a/oauth-provider-api/oauth-provider-api.iml +++ b/oauth-provider-api/oauth-provider-api.iml @@ -12,6 +12,10 @@ + + + + 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 a75dc50..c7b5969 100644 --- a/oauth-provider/oauth-provider.iml +++ b/oauth-provider/oauth-provider.iml @@ -40,9 +40,6 @@ - - - @@ -72,7 +69,6 @@ - @@ -90,6 +86,10 @@ + + + + diff --git a/oauth-provider/pom.xml b/oauth-provider/pom.xml index 3018777..4744a12 100644 --- a/oauth-provider/pom.xml +++ b/oauth-provider/pom.xml @@ -44,6 +44,10 @@ mysql-connector-java runtime + + org.hibernate + hibernate-validator + com.h2database h2 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 index 2632868..7df7b5d 100644 --- 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 @@ -11,6 +11,8 @@ import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.web.bind.annotation.*; +import javax.validation.Valid; + /** * @author Valentin Zickner */ @@ -26,10 +28,8 @@ public class UserManagementController implements OAuthProviderUserManagementApi @Override @RequestMapping(value = CREATE, method = RequestMethod.POST) - public UserResponseDto create(@RequestBody UserRequestDto userRequestDto) { - if (oAuthUserRepository.findOneByUsernameIgnoreCase(userRequestDto.getUsername()) != null) { - throw new UsernameAlreadyExistsException(); - } + public UserResponseDto create(@Valid @RequestBody UserRequestDto userRequestDto) { + assertUserDoesNotExists(userRequestDto); String password = passwordEncoder.encode(userRequestDto.getPassword()); OAuthUser user = oAuthUserRepository.save(new OAuthUser(userRequestDto.getUsername(), password)); @@ -39,8 +39,22 @@ public UserResponseDto create(@RequestBody UserRequestDto userRequestDto) { @Override @RequestMapping(value = UPDATE, method = RequestMethod.PUT) @ResponseStatus(HttpStatus.NO_CONTENT) - public void update(@PathVariable("id") String id, UserRequestDto userRequestDto) { - throw new RuntimeException("Not yet implemented"); + 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 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 index 7b037ea..d77ce26 100644 --- 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 @@ -16,6 +16,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; 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.*; /** @@ -38,10 +39,6 @@ public void setUp() throws Exception { @Test public void create_withAdminAndUsernameAndPassword_returnsNewUserId() throws Exception { - // Arrange - String username = "my-user"; - String password = "my-password"; - // Act MvcResult result = this.mockMvc.perform(post(RESOURCE_PREFIX + OAuthProviderUserManagementApi.CREATE) .header("Authorization", getAuthorizationAdmin()) @@ -61,8 +58,32 @@ public void create_withAdminAndUsernameAndPassword_returnsNewUserId() throws Exc String userId = userResponseDto.getId(); assertNotNull(userId); OAuthUser oAuthUser = oAuthUserRepository.findOne(Long.valueOf(userId)); - assertEquals(username, oAuthUser.getUsername()); - assertTrue(passwordEncoder.matches(password, oAuthUser.getPassword())); + 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 @@ -120,4 +141,90 @@ public void create_withNoAuthentication_returns401() throws Exception { ).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); + } + } \ No newline at end of file diff --git a/pom.xml b/pom.xml index 0cfe084..d444f0a 100644 --- a/pom.xml +++ b/pom.xml @@ -12,6 +12,7 @@ 1.8 4.12 + 5.2.4.Final From ec4daf0b372287d29603e3faebd482109493af85 Mon Sep 17 00:00:00 2001 From: Valentin Zickner Date: Fri, 10 Jun 2016 21:22:36 +0200 Subject: [PATCH 15/17] Implement deletion of users. --- .../rest/UserManagementController.java | 2 +- .../oauth/provider/MockMvcOAuthLogin.java | 34 +++++++++++-------- .../rest/UserManagementControllerTest.java | 32 +++++++++++++++++ 3 files changed, 53 insertions(+), 15 deletions(-) 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 index 7df7b5d..9bf0d9f 100644 --- 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 @@ -61,6 +61,6 @@ private void assertUserDoesNotExists(@RequestBody UserRequestDto userRequestDto) @RequestMapping(value = DELETE, method = RequestMethod.DELETE) @ResponseStatus(HttpStatus.NO_CONTENT) public void delete(@PathVariable("id") String id) { - throw new RuntimeException("Not yet implemented"); + oAuthUserRepository.delete(Long.valueOf(id)); } } \ 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 index 81eb04f..bfcbbbc 100644 --- a/oauth-provider/src/test/java/de/helfenkannjeder/oauth/provider/MockMvcOAuthLogin.java +++ b/oauth-provider/src/test/java/de/helfenkannjeder/oauth/provider/MockMvcOAuthLogin.java @@ -8,6 +8,7 @@ import org.springframework.mock.web.MockHttpSession; 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; @@ -55,26 +56,31 @@ public String getAccessTokenWithClientSecret(String clientId, String secret) thr public String getAccessTokenWithAuthorizationCode(String clientId, String secret, String username, String password) throws Exception { MockHttpSession mockSession = new MockHttpSession(webApplicationContext.getServletContext(), UUID.randomUUID().toString()); - mockMvc.perform( + ResultActions query = mockMvc.perform( get(ENDPOINT_OAUTH_AUTHORIZATION) .session(mockSession) .header("Authorization", createBase64Auth(username, password)) .param("response_type", "code") .param("redirect_uri", "/") .param("client_id", clientId) - ) - .andExpect(status().isOk()) - .andExpect(forwardedUrl(ENDPOINT_CONFIRM_ACCESS)) - .andReturn().getResponse(); - - MockHttpServletResponse response = mockMvc.perform( - post(ENDPOINT_OAUTH_AUTHORIZATION) - .session(mockSession) - .header("Authorization", createBase64Auth(username, password)) - .param("scope.default", "true") - .param("user_oauth_approval", "true") - .param("authorize", "Authorize") - ) + ); + MockHttpServletResponse response = query.andReturn().getResponse(); + + // needs to confirm access + if (response.getStatus() == 200) { + query.andExpect(forwardedUrl(ENDPOINT_CONFIRM_ACCESS)); + + query = mockMvc.perform( + post(ENDPOINT_OAUTH_AUTHORIZATION) + .session(mockSession) + .header("Authorization", createBase64Auth(username, password)) + .param("scope.default", "true") + .param("user_oauth_approval", "true") + .param("authorize", "Authorize") + ); + } + + response = query .andExpect(status().is3xxRedirection()) .andReturn().getResponse(); 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 index d77ce26..7376912 100644 --- 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 @@ -14,7 +14,9 @@ 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.*; @@ -227,4 +229,34 @@ public void update_withAdminLoggedInEmptyObject_verifyUserIsNotChanged() throws 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 From 34228f6e41544d8572454de516dd750fea1d5e6b Mon Sep 17 00:00:00 2001 From: Valentin Zickner Date: Sat, 18 Jun 2016 17:59:31 +0200 Subject: [PATCH 16/17] Implement password authorization against OAuth authorization server. --- .../OAuth2ProviderConfiguration.java | 36 +++++++++----- .../ResourceServerConfiguration.java | 4 +- .../WebSecurityConfiguration.java | 48 +++++++++++++++++++ .../oauth/provider/MockMvcOAuthLogin.java | 21 +++++++- .../rest/AbstractOAuthControllerTest.java | 4 ++ .../rest/SimpleAuthorizationTest.java | 43 +++++++++++++++++ .../provider/rest/UserControllerTest.java | 13 +++++ 7 files changed, 155 insertions(+), 14 deletions(-) create mode 100644 oauth-provider/src/main/java/de/helfenkannjeder/oauth/provider/configuration/WebSecurityConfiguration.java create mode 100644 oauth-provider/src/test/java/de/helfenkannjeder/oauth/provider/rest/SimpleAuthorizationTest.java 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 index 078c022..320a84f 100644 --- 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 @@ -2,10 +2,12 @@ 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.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +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; @@ -13,6 +15,7 @@ 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; @@ -42,6 +45,13 @@ public class OAuth2ProviderConfiguration extends AuthorizationServerConfigurerAd @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 @@ -55,7 +65,7 @@ public void configure(ClientDetailsServiceConfigurer clients) throws Exception { .and() .withClient(come2helpClientId) .secret(come2helpClientSecret) - .authorizedGrantTypes(AUTHORIZATION_CODE, REFRESH_TOKEN) + .authorizedGrantTypes(AUTHORIZATION_CODE, PASSWORD, REFRESH_TOKEN) .scopes(SCOPE_DEFAULT) .authorities(OAuthProviderAuthority.ROLE_USER.getAuthority()); // @formatter:on @@ -63,7 +73,10 @@ public void configure(ClientDetailsServiceConfigurer clients) throws Exception { @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { - endpoints.tokenStore(tokenStore); + endpoints + .authenticationManager(authenticationManager) + .userDetailsService(userDetailsService) + .tokenStore(tokenStore); } @Override @@ -71,16 +84,17 @@ public void configure(AuthorizationServerSecurityConfigurer security) throws Exc security.allowFormAuthenticationForClients(); } - @Autowired - public void configureGlobal(AuthenticationManagerBuilder authenticationManagerBuilder, - UserDetailsService userDetailsService) throws Exception { - authenticationManagerBuilder - .userDetailsService(userDetailsService) - .passwordEncoder(passwordEncoder()); - } - @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 index 13dd3bb..f3b6f5e 100644 --- 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 @@ -13,8 +13,10 @@ public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter { @Override public void configure(HttpSecurity http) throws Exception { + // @formatter:off http.authorizeRequests() .antMatchers("/admin/**").hasAuthority("ROLE_ADMIN") - .antMatchers("/**").authenticated(); + .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/test/java/de/helfenkannjeder/oauth/provider/MockMvcOAuthLogin.java b/oauth-provider/src/test/java/de/helfenkannjeder/oauth/provider/MockMvcOAuthLogin.java index bfcbbbc..af70f65 100644 --- a/oauth-provider/src/test/java/de/helfenkannjeder/oauth/provider/MockMvcOAuthLogin.java +++ b/oauth-provider/src/test/java/de/helfenkannjeder/oauth/provider/MockMvcOAuthLogin.java @@ -6,6 +6,8 @@ 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; @@ -36,6 +38,10 @@ public class MockMvcOAuthLogin { 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; @@ -56,10 +62,11 @@ public String getAccessTokenWithClientSecret(String clientId, String secret) thr public String 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", createBase64Auth(username, password)) + .header("Authorization", authorization) .param("response_type", "code") .param("redirect_uri", "/") .param("client_id", clientId) @@ -70,10 +77,20 @@ public String getAccessTokenWithAuthorizationCode(String clientId, String secret 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", createBase64Auth(username, password)) + .header("Authorization", authorization) + .header(csrfToken.getHeaderName(), csrfToken.getToken()) .param("scope.default", "true") .param("user_oauth_approval", "true") .param("authorize", "Authorize") 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 index 05bde44..adc499a 100644 --- 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 @@ -60,4 +60,8 @@ protected String getAuthorizationAdmin() throws Exception { protected String getAuthorizationDefaultUser() throws Exception { return mockMvcOAuthLogin.getAccessTokenWithAuthorizationCode("come2help-web", "secret", AbstractOAuthControllerTest.DEFAULT_USER, AbstractOAuthControllerTest.DEFAULT_PASSWORD); } + + protected String getAuthorizationDefaultUserWithPassword() throws Exception { + return mockMvcOAuthLogin.getAccessTokenWithPassword("come2help-web", "secret", AbstractOAuthControllerTest.DEFAULT_USER, AbstractOAuthControllerTest.DEFAULT_PASSWORD); + } } 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..3e28bd0 --- /dev/null +++ b/oauth-provider/src/test/java/de/helfenkannjeder/oauth/provider/rest/SimpleAuthorizationTest.java @@ -0,0 +1,43 @@ +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()); + } +} 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 index aa27095..3de42d0 100644 --- 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 @@ -28,6 +28,19 @@ public void currentUser_withNormalUser_expectUsername() throws Exception { .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 From cd53fbbba3461e7020bbe274e464fa77063253c6 Mon Sep 17 00:00:00 2001 From: Valentin Zickner Date: Sat, 18 Jun 2016 18:20:59 +0200 Subject: [PATCH 17/17] Add test for refresh_token endpoint. --- .../oauth/provider/MockMvcOAuthLogin.java | 43 ++++++++++++++++--- .../rest/AbstractOAuthControllerTest.java | 12 +++--- .../rest/SimpleAuthorizationTest.java | 9 ++++ 3 files changed, 53 insertions(+), 11 deletions(-) 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 index af70f65..d290c88 100644 --- a/oauth-provider/src/test/java/de/helfenkannjeder/oauth/provider/MockMvcOAuthLogin.java +++ b/oauth-provider/src/test/java/de/helfenkannjeder/oauth/provider/MockMvcOAuthLogin.java @@ -55,11 +55,11 @@ public MockMvcOAuthLogin(WebApplicationContext webApplicationContext, Filter spr } - public String getAccessTokenWithClientSecret(String clientId, String secret) throws Exception { + public OAuthInformation getAccessTokenWithClientSecret(String clientId, String secret) throws Exception { return getAccessToken(post(ENDPOINT_OAUTH_TOKEN), OAuth2ProviderConfiguration.CLIENT_CREDENTIALS, clientId, secret); } - public String getAccessTokenWithAuthorizationCode(String clientId, String secret, String username, String password) throws Exception { + 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); @@ -106,24 +106,30 @@ public String getAccessTokenWithAuthorizationCode(String clientId, String secret return getAccessTokenWithAuthorizationCode(clientId, secret, code); } - private String getAccessTokenWithAuthorizationCode(String clientId, String secret, String code) throws Exception { + 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 String getAccessTokenWithPassword(String clientId, String secret, String username, String password) throws Exception { + 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 String getAccessToken(MockHttpServletRequestBuilder post, String grantType, String clientId, String secret) throws Exception { + private OAuthInformation getAccessToken(MockHttpServletRequestBuilder post, String grantType, String clientId, String secret) throws Exception { // @formatter:off String content = mockMvc.perform(post @@ -142,7 +148,10 @@ private String getAccessToken(MockHttpServletRequestBuilder post, String grantTy ObjectMapper objectMapper = new ObjectMapper(); HashMap result = objectMapper.readValue(content, HashMap.class); - return "Bearer " + result.get("access_token"); + return new OAuthInformation( + (String) result.get("access_token"), + (String) result.get("refresh_token") + ); } private String createBase64Auth(String username, String password) { @@ -150,4 +159,26 @@ private String createBase64Auth(String username, String password) { } + 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 index adc499a..ab67281 100644 --- 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 @@ -27,12 +27,13 @@ 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 + @Autowired PasswordEncoder passwordEncoder; @Autowired @@ -40,8 +41,9 @@ public abstract class AbstractOAuthControllerTest { @Autowired private Filter springSecurityFilterChain; + @Autowired - private MockMvcOAuthLogin mockMvcOAuthLogin; + MockMvcOAuthLogin mockMvcOAuthLogin; @Before public void initMockMvc() throws Exception { @@ -54,14 +56,14 @@ public void initMockMvc() throws Exception { } protected String getAuthorizationAdmin() throws Exception { - return mockMvcOAuthLogin.getAccessTokenWithClientSecret("oauth-provider-admin", "default-secret"); + 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); + 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); + 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 index 3e28bd0..20ca103 100644 --- 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 @@ -40,4 +40,13 @@ public void loginAuthorizationEndpoint_withoutPassword_returnsUnauthorized() thr ) .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()); + } }