diff --git a/core/src/main/java/org/springframework/security/authentication/MapUserDetailsRepository.java b/core/src/main/java/org/springframework/security/authentication/MapUserDetailsRepository.java index 4e4ec82a30..c5bb4a6424 100644 --- a/core/src/main/java/org/springframework/security/authentication/MapUserDetailsRepository.java +++ b/core/src/main/java/org/springframework/security/authentication/MapUserDetailsRepository.java @@ -18,6 +18,7 @@ package org.springframework.security.authentication; +import java.util.Arrays; import java.util.Collection; import java.util.Map; import java.util.function.Function; @@ -37,6 +38,14 @@ import reactor.core.publisher.Mono; public class MapUserDetailsRepository implements UserDetailsRepository { private final Map users; + public MapUserDetailsRepository(Map users) { + this.users = users; + } + + public MapUserDetailsRepository(UserDetails... users) { + this(Arrays.asList(users)); + } + public MapUserDetailsRepository(Collection users) { Assert.notEmpty(users, "users cannot be null or empty"); this.users = users.stream().collect(Collectors.toMap( u -> getKey(u.getName()), Function.identity())); diff --git a/samples/javaconfig/hellowebflux/src/integration-test/java/sample/HelloWebfluxApplicationITests.java b/samples/javaconfig/hellowebflux/src/integration-test/java/sample/HelloWebfluxApplicationITests.java index 1b11efc5f4..f6e79677f1 100644 --- a/samples/javaconfig/hellowebflux/src/integration-test/java/sample/HelloWebfluxApplicationITests.java +++ b/samples/javaconfig/hellowebflux/src/integration-test/java/sample/HelloWebfluxApplicationITests.java @@ -70,10 +70,10 @@ public class HelloWebfluxApplicationITests { this.rest .filter(robsCredentials()) .get() - .uri("/users") + .uri("/principal") .exchange() .expectStatus().isOk() - .expectBody().json("[{\"id\":null,\"username\":\"rob\",\"password\":\"rob\",\"firstname\":\"Rob\",\"lastname\":\"Winch\"},{\"id\":null,\"username\":\"admin\",\"password\":\"admin\",\"firstname\":\"Admin\",\"lastname\":\"User\"}]"); + .expectBody().json("{\"username\":\"rob\"}"); } @Test @@ -81,7 +81,7 @@ public class HelloWebfluxApplicationITests { this.rest .filter(invalidPassword()) .get() - .uri("/users") + .uri("/principal") .exchange() .expectStatus().isUnauthorized() .expectBody().isEmpty(); @@ -143,7 +143,7 @@ public class HelloWebfluxApplicationITests { ExchangeResult result = this.rest .filter(robsCredentials()) .get() - .uri("/users") + .uri("/principal") .exchange() .returnResult(String.class); @@ -151,7 +151,7 @@ public class HelloWebfluxApplicationITests { this.rest .get() - .uri("/users") + .uri("/principal") .header("Cookie", session) .exchange() .expectStatus().isOk(); diff --git a/samples/javaconfig/hellowebflux/src/integration-test/java/sample/UserRepositoryTests.java b/samples/javaconfig/hellowebflux/src/integration-test/java/sample/UserRepositoryTests.java deleted file mode 100644 index 7bb63bf49f..0000000000 --- a/samples/javaconfig/hellowebflux/src/integration-test/java/sample/UserRepositoryTests.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2002-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package sample; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.TestPropertySource; -import org.springframework.test.context.junit4.SpringRunner; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * - * @author Rob Winch - * @since 5.0 - */ -@SuppressWarnings("unused") -@RunWith(SpringRunner.class) -@ContextConfiguration(classes = HelloWebfluxApplication.class) -@TestPropertySource(properties = "server.port=0") -public class UserRepositoryTests { - - @Autowired UserRepository repository; - - String robUsername = "rob"; - - @Test - public void findByUsernameWhenUsernameMatchesThenFound() { - assertThat(repository.findByUsername(this.robUsername).block()).isNotNull(); - } - - @Test - public void findByUsernameWhenUsernameDoesNotMatchThenFound() { - assertThat(repository.findByUsername(this.robUsername + "NOTFOUND").block()).isNull(); - } -} diff --git a/samples/javaconfig/hellowebflux/src/main/java/sample/MapUserRepository.java b/samples/javaconfig/hellowebflux/src/main/java/sample/MapUserRepository.java deleted file mode 100644 index ed775a1b33..0000000000 --- a/samples/javaconfig/hellowebflux/src/main/java/sample/MapUserRepository.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * - * * Copyright 2002-2017 the original author or authors. - * * - * * Licensed under the Apache License, Version 2.0 (the "License"); - * * you may not use this file except in compliance with the License. - * * You may obtain a copy of the License at - * * - * * http://www.apache.org/licenses/LICENSE-2.0 - * * - * * Unless required by applicable law or agreed to in writing, software - * * distributed under the License is distributed on an "AS IS" BASIS, - * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * * See the License for the specific language governing permissions and - * * limitations under the License. - * - */ - -package sample; - -import java.util.HashMap; -import java.util.Map; - -import org.springframework.stereotype.Service; - -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -/** - * @author Rob Winch - * @since 5.0 - */ -@Service -public class MapUserRepository implements UserRepository { - private final Map users = new HashMap<>(); - - public MapUserRepository() { - save(new User("rob", "rob", "Rob", "Winch")).block(); - save(new User("admin", "admin", "Admin", "User")).block(); - } - - @Override - public Flux findAll() { - return Flux.fromIterable(users.values()); - } - - @Override - public Mono findByUsername(String username) { - User result = users.get(username); - - return result == null ? Mono.empty() : Mono.just(result); - } - - public Mono save(User user) { - users.put(user.getUsername(), user); - return Mono.just(user); - } -} diff --git a/samples/javaconfig/hellowebflux/src/main/java/sample/SecurityConfig.java b/samples/javaconfig/hellowebflux/src/main/java/sample/SecurityConfig.java index 8f64fc7070..2897de6361 100644 --- a/samples/javaconfig/hellowebflux/src/main/java/sample/SecurityConfig.java +++ b/samples/javaconfig/hellowebflux/src/main/java/sample/SecurityConfig.java @@ -19,10 +19,13 @@ package sample; import org.springframework.context.annotation.Bean; +import org.springframework.security.authentication.MapUserDetailsRepository; import org.springframework.security.authorization.AuthorizationDecision; import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity; import org.springframework.security.config.web.server.HttpSecurity; import org.springframework.security.core.Authentication; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.web.server.authorization.AuthorizationContext; import org.springframework.web.server.WebFilter; import reactor.core.publisher.Mono; @@ -49,4 +52,12 @@ public class SecurityConfig { .map( a -> context.getVariables().get("user").equals(a.getName())) .map( granted -> new AuthorizationDecision(granted)); } + + @Bean + public MapUserDetailsRepository userDetailsRepository() { + UserDetails rob = User.withUsername("rob").password("rob").roles("USER").build(); + UserDetails admin = User.withUsername("admin").password("admin").roles("USER","ADMIN").build(); + return new MapUserDetailsRepository(rob, admin); + } + } diff --git a/samples/javaconfig/hellowebflux/src/main/java/sample/User.java b/samples/javaconfig/hellowebflux/src/main/java/sample/User.java deleted file mode 100644 index ef81772c6b..0000000000 --- a/samples/javaconfig/hellowebflux/src/main/java/sample/User.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2002-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package sample; - -/** - * @author Rob Winch - * @since 5.0 - */ -public class User { - - private Long id; - private String username; - private String password; - private String firstname; - private String lastname; - - public User() {} - - public User(User copy) { - this(copy.getUsername(), copy.getPassword(), copy.getFirstname(), copy.getLastname()); - } - - public User(String username, String password, String firstname, String lastname) { - super(); - this.username = username; - this.password = password; - this.firstname = firstname; - this.lastname = lastname; - } - - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public String getUsername() { - return username; - } - - public void setUsername(String username) { - this.username = username; - } - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } - - public String getFirstname() { - return firstname; - } - - public void setFirstname(String firstname) { - this.firstname = firstname; - } - - public String getLastname() { - return lastname; - } - - public void setLastname(String lastname) { - this.lastname = lastname; - } -} diff --git a/samples/javaconfig/hellowebflux/src/main/java/sample/UserController.java b/samples/javaconfig/hellowebflux/src/main/java/sample/UserController.java index f8fe22aae9..fb9192fbb4 100644 --- a/samples/javaconfig/hellowebflux/src/main/java/sample/UserController.java +++ b/samples/javaconfig/hellowebflux/src/main/java/sample/UserController.java @@ -16,37 +16,31 @@ package sample; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.server.WebSession; +import reactor.core.publisher.Mono; + import java.security.Principal; import java.util.Collections; import java.util.Map; -import org.springframework.security.core.annotation.AuthenticationPrincipal; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RestController; - -import org.springframework.web.server.WebSession; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - /** * @author Rob Winch * @since 5.0 */ @RestController public class UserController { - private final UserRepository users; - - public UserController(UserRepository users) { - this.users = users; - } @GetMapping("/me") - public Mono> me(@AuthenticationPrincipal User user) { + public Mono> me(@AuthenticationPrincipal UserDetails user) { return me(Mono.just(user)); } @GetMapping("/mono/me") - public Mono> me(@AuthenticationPrincipal Mono user) { + public Mono> me(@AuthenticationPrincipal Mono user) { return user.flatMap( u -> Mono.just(Collections.singletonMap("username", u.getUsername()))); } @@ -55,11 +49,6 @@ public class UserController { return session.flatMap( s -> Mono.just(s.getAttributes())); } - @GetMapping("/users") - public Flux users() { - return this.users.findAll(); - } - @GetMapping("/principal") public Mono> principal(Principal principal) { return principal(Mono.just(principal)); diff --git a/samples/javaconfig/hellowebflux/src/main/java/sample/UserRepository.java b/samples/javaconfig/hellowebflux/src/main/java/sample/UserRepository.java deleted file mode 100644 index 58fd96b437..0000000000 --- a/samples/javaconfig/hellowebflux/src/main/java/sample/UserRepository.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2002-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package sample; - -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -/** - * - * @author Rob Winch - * @since 5.0 - */ -public interface UserRepository { - - Flux findAll(); - - Mono findByUsername(String username); - - Mono save(User user); -} diff --git a/samples/javaconfig/hellowebflux/src/main/java/sample/UserRepositoryUserDetailsRepository.java b/samples/javaconfig/hellowebflux/src/main/java/sample/UserRepositoryUserDetailsRepository.java deleted file mode 100644 index d475e7b10b..0000000000 --- a/samples/javaconfig/hellowebflux/src/main/java/sample/UserRepositoryUserDetailsRepository.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2002-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package sample; - -import java.util.Collection; -import java.util.List; - -import org.springframework.security.authentication.UserDetailsRepository; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.AuthorityUtils; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.stereotype.Component; - -import reactor.core.publisher.Mono; - -/** - * @author Rob Winch - * @since 5.0 - */ -@Component -public class UserRepositoryUserDetailsRepository implements UserDetailsRepository { - private final UserRepository users; - - public UserRepositoryUserDetailsRepository(UserRepository users) { - super(); - this.users = users; - } - - @Override - public Mono findByUsername(String username) { - return this.users - .findByUsername(username) - .map(UserDetailsAdapter::new); - } - - @SuppressWarnings("serial") - private static class UserDetailsAdapter extends User implements UserDetails { - private static List USER_ROLES = AuthorityUtils.createAuthorityList("ROLE_USER"); - private static List ADMIN_ROLES = AuthorityUtils.createAuthorityList("ROLE_ADMIN", "ROLE_USER"); - - private UserDetailsAdapter(User delegate) { - super(delegate); - } - - @Override - public Collection getAuthorities() { - return isAdmin() ? ADMIN_ROLES : USER_ROLES ; - } - - private boolean isAdmin() { - return getUsername().contains("admin"); - } - - @Override - public boolean isAccountNonExpired() { - return true; - } - - @Override - public boolean isAccountNonLocked() { - return true; - } - - @Override - public boolean isCredentialsNonExpired() { - return true; - } - - @Override - public boolean isEnabled() { - return true; - } - } -} diff --git a/samples/javaconfig/hellowebflux/src/test/java/sample/HelloWebfluxApplicationTests.java b/samples/javaconfig/hellowebflux/src/test/java/sample/HelloWebfluxApplicationTests.java index 9b718905db..091c3f05ea 100644 --- a/samples/javaconfig/hellowebflux/src/test/java/sample/HelloWebfluxApplicationTests.java +++ b/samples/javaconfig/hellowebflux/src/test/java/sample/HelloWebfluxApplicationTests.java @@ -61,7 +61,7 @@ public class HelloWebfluxApplicationTests { public void basicRequired() throws Exception { this.rest .get() - .uri("/users") + .uri("/principal") .exchange() .expectStatus().isUnauthorized(); } @@ -71,10 +71,10 @@ public class HelloWebfluxApplicationTests { this.rest .filter(robsCredentials()) .get() - .uri("/users") + .uri("/principal") .exchange() .expectStatus().isOk() - .expectBody().json("[{\"id\":null,\"username\":\"rob\",\"password\":\"rob\",\"firstname\":\"Rob\",\"lastname\":\"Winch\"},{\"id\":null,\"username\":\"admin\",\"password\":\"admin\",\"firstname\":\"Admin\",\"lastname\":\"User\"}]"); + .expectBody().json("{\"username\":\"rob\"}"); } @Test @@ -82,7 +82,7 @@ public class HelloWebfluxApplicationTests { this.rest .filter(invalidPassword()) .get() - .uri("/users") + .uri("/principal") .exchange() .expectStatus().isUnauthorized() .expectBody().isEmpty(); @@ -144,7 +144,7 @@ public class HelloWebfluxApplicationTests { ExchangeResult result = this.rest .filter(robsCredentials()) .get() - .uri("/users") + .uri("/principal") .exchange() .returnResult(String.class); @@ -152,7 +152,7 @@ public class HelloWebfluxApplicationTests { this.rest .get() - .uri("/users") + .uri("/principal") .cookie(session.getName(), session.getValue()) .exchange() .expectStatus().isOk(); @@ -163,13 +163,13 @@ public class HelloWebfluxApplicationTests { this.rest .exchangeMutator( withUser() ) .get() - .uri("/users") + .uri("/principal") .exchange() .expectStatus().isOk(); this.rest .get() - .uri("/users") + .uri("/principal") .exchange() .expectStatus().isUnauthorized(); } diff --git a/samples/javaconfig/hellowebfluxfn/src/integration-test/java/sample/HelloWebfluxFnApplicationITests.java b/samples/javaconfig/hellowebfluxfn/src/integration-test/java/sample/HelloWebfluxFnApplicationITests.java index b1a2bea584..1c3c0dffdc 100644 --- a/samples/javaconfig/hellowebfluxfn/src/integration-test/java/sample/HelloWebfluxFnApplicationITests.java +++ b/samples/javaconfig/hellowebfluxfn/src/integration-test/java/sample/HelloWebfluxFnApplicationITests.java @@ -21,6 +21,7 @@ import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseCookie; import org.springframework.security.web.server.header.ContentTypeOptionsHttpHeadersWriter; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.TestPropertySource; @@ -33,6 +34,7 @@ import java.nio.charset.Charset; import java.time.Duration; import java.util.Base64; +import static org.springframework.security.test.web.reactive.server.SecurityExchangeMutators.withUser; import static org.springframework.web.reactive.function.client.ExchangeFilterFunctions.basicAuthentication; /** @@ -70,10 +72,10 @@ public class HelloWebfluxFnApplicationITests { this.rest .filter(robsCredentials()) .get() - .uri("/users") + .uri("/principal") .exchange() .expectStatus().isOk() - .expectBody().json("[{\"id\":null,\"username\":\"rob\",\"password\":\"rob\",\"firstname\":\"Rob\",\"lastname\":\"Winch\"},{\"id\":null,\"username\":\"admin\",\"password\":\"admin\",\"firstname\":\"Admin\",\"lastname\":\"User\"}]"); + .expectBody().json("{\"username\":\"rob\"}"); } @Test @@ -81,7 +83,7 @@ public class HelloWebfluxFnApplicationITests { this.rest .filter(invalidPassword()) .get() - .uri("/users") + .uri("/principal") .exchange() .expectStatus().isUnauthorized() .expectBody().isEmpty(); @@ -141,17 +143,17 @@ public class HelloWebfluxFnApplicationITests { @Test public void sessionWorks() throws Exception { ExchangeResult result = this.rest - .filter(robsCredentials()) - .get() - .uri("/users") - .exchange() - .returnResult(String.class); + .filter(robsCredentials()) + .get() + .uri("/principal") + .exchange() + .returnResult(String.class); String session = result.getResponseHeaders().getFirst("Set-Cookie"); this.rest .get() - .uri("/users") + .uri("/principal") .header("Cookie", session) .exchange() .expectStatus().isOk(); @@ -171,14 +173,14 @@ public class HelloWebfluxFnApplicationITests { @Test public void headers() throws Exception { this.rest - .filter(robsCredentials()) - .get() - .uri("/principal") - .exchange() - .expectHeader().valueEquals(HttpHeaders.CACHE_CONTROL, "no-cache, no-store, max-age=0, must-revalidate") - .expectHeader().valueEquals(HttpHeaders.EXPIRES, "0") - .expectHeader().valueEquals(HttpHeaders.PRAGMA, "no-cache") - .expectHeader().valueEquals(ContentTypeOptionsHttpHeadersWriter.X_CONTENT_OPTIONS, ContentTypeOptionsHttpHeadersWriter.NOSNIFF); + .filter(robsCredentials()) + .get() + .uri("/principal") + .exchange() + .expectHeader().valueEquals(HttpHeaders.CACHE_CONTROL, "no-cache, no-store, max-age=0, must-revalidate") + .expectHeader().valueEquals(HttpHeaders.EXPIRES, "0") + .expectHeader().valueEquals(HttpHeaders.PRAGMA, "no-cache") + .expectHeader().valueEquals(ContentTypeOptionsHttpHeadersWriter.X_CONTENT_OPTIONS, ContentTypeOptionsHttpHeadersWriter.NOSNIFF); } private ExchangeFilterFunction robsCredentials() { diff --git a/samples/javaconfig/hellowebfluxfn/src/integration-test/java/sample/UserRepositoryTests.java b/samples/javaconfig/hellowebfluxfn/src/integration-test/java/sample/UserRepositoryTests.java deleted file mode 100644 index 1ba839f082..0000000000 --- a/samples/javaconfig/hellowebfluxfn/src/integration-test/java/sample/UserRepositoryTests.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2002-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package sample; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.TestPropertySource; -import org.springframework.test.context.junit4.SpringRunner; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * - * @author Rob Winch - * @since 5.0 - */ -@SuppressWarnings("unused") -@RunWith(SpringRunner.class) -@ContextConfiguration(classes = HelloWebfluxFnApplication.class) -@TestPropertySource(properties = "server.port=0") -public class UserRepositoryTests { - - @Autowired UserRepository repository; - - String robUsername = "rob"; - - @Test - public void findByUsernameWhenUsernameMatchesThenFound() { - assertThat(repository.findByUsername(this.robUsername).block()).isNotNull(); - } - - @Test - public void findByUsernameWhenUsernameDoesNotMatchThenFound() { - assertThat(repository.findByUsername(this.robUsername + "NOTFOUND").block()).isNull(); - } -} diff --git a/samples/javaconfig/hellowebfluxfn/src/main/java/sample/HelloWebfluxFnApplication.java b/samples/javaconfig/hellowebfluxfn/src/main/java/sample/HelloWebfluxFnApplication.java index b4ba39a8fe..79372c09fb 100644 --- a/samples/javaconfig/hellowebfluxfn/src/main/java/sample/HelloWebfluxFnApplication.java +++ b/samples/javaconfig/hellowebfluxfn/src/main/java/sample/HelloWebfluxFnApplication.java @@ -16,40 +16,20 @@ package sample; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.*; -import org.springframework.core.ReactiveAdapterRegistry; import org.springframework.http.server.reactive.HttpHandler; import org.springframework.http.server.reactive.ReactorHttpHandlerAdapter; -import org.springframework.security.authentication.ReactiveAuthenticationManager; -import org.springframework.security.authentication.UserDetailsRepositoryAuthenticationManager; -import org.springframework.security.authorization.AuthorizationDecision; -import org.springframework.security.config.web.server.AuthorizeExchangeBuilder; -import org.springframework.security.config.web.server.HttpSecurity; -import org.springframework.security.core.Authentication; -import org.springframework.security.web.reactive.result.method.annotation.AuthenticationPrincipalArgumentResolver; -import org.springframework.security.web.server.authorization.AuthorizationContext; -import org.springframework.security.web.server.context.WebSessionSecurityContextRepository; -import org.springframework.web.reactive.DispatcherHandler; import org.springframework.web.reactive.config.EnableWebFlux; -import org.springframework.web.reactive.config.WebFluxConfigurer; import org.springframework.web.reactive.function.server.HandlerStrategies; import org.springframework.web.reactive.function.server.RouterFunction; import org.springframework.web.reactive.function.server.RouterFunctions; import org.springframework.web.reactive.function.server.ServerResponse; -import org.springframework.web.reactive.result.method.annotation.ArgumentResolverConfigurer; import org.springframework.web.server.WebFilter; -import reactor.core.publisher.Mono; import reactor.ipc.netty.NettyContext; import reactor.ipc.netty.http.server.HttpServer; -import static org.springframework.http.MediaType.APPLICATION_JSON; -import static org.springframework.security.config.web.server.HttpSecurity.http; import static org.springframework.web.reactive.function.server.RequestPredicates.GET; -import static org.springframework.web.reactive.function.server.RequestPredicates.accept; -import static org.springframework.web.reactive.function.server.RequestPredicates.contentType; import static org.springframework.web.reactive.function.server.RouterFunctions.route; /** @@ -81,7 +61,6 @@ public class HelloWebfluxFnApplication { public HttpHandler httpHandler(UserController userController, WebFilter springSecurityFilterChain) { RouterFunction route = route( GET("/principal"), userController::principal).andRoute( - GET("/users"), userController::users).andRoute( GET("/admin"), userController::admin); HandlerStrategies handlerStrategies = HandlerStrategies.builder() diff --git a/samples/javaconfig/hellowebfluxfn/src/main/java/sample/MapUserRepository.java b/samples/javaconfig/hellowebfluxfn/src/main/java/sample/MapUserRepository.java deleted file mode 100644 index ed775a1b33..0000000000 --- a/samples/javaconfig/hellowebfluxfn/src/main/java/sample/MapUserRepository.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * - * * Copyright 2002-2017 the original author or authors. - * * - * * Licensed under the Apache License, Version 2.0 (the "License"); - * * you may not use this file except in compliance with the License. - * * You may obtain a copy of the License at - * * - * * http://www.apache.org/licenses/LICENSE-2.0 - * * - * * Unless required by applicable law or agreed to in writing, software - * * distributed under the License is distributed on an "AS IS" BASIS, - * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * * See the License for the specific language governing permissions and - * * limitations under the License. - * - */ - -package sample; - -import java.util.HashMap; -import java.util.Map; - -import org.springframework.stereotype.Service; - -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -/** - * @author Rob Winch - * @since 5.0 - */ -@Service -public class MapUserRepository implements UserRepository { - private final Map users = new HashMap<>(); - - public MapUserRepository() { - save(new User("rob", "rob", "Rob", "Winch")).block(); - save(new User("admin", "admin", "Admin", "User")).block(); - } - - @Override - public Flux findAll() { - return Flux.fromIterable(users.values()); - } - - @Override - public Mono findByUsername(String username) { - User result = users.get(username); - - return result == null ? Mono.empty() : Mono.just(result); - } - - public Mono save(User user) { - users.put(user.getUsername(), user); - return Mono.just(user); - } -} diff --git a/samples/javaconfig/hellowebfluxfn/src/main/java/sample/SecurityConfig.java b/samples/javaconfig/hellowebfluxfn/src/main/java/sample/SecurityConfig.java index 8f64fc7070..2897de6361 100644 --- a/samples/javaconfig/hellowebfluxfn/src/main/java/sample/SecurityConfig.java +++ b/samples/javaconfig/hellowebfluxfn/src/main/java/sample/SecurityConfig.java @@ -19,10 +19,13 @@ package sample; import org.springframework.context.annotation.Bean; +import org.springframework.security.authentication.MapUserDetailsRepository; import org.springframework.security.authorization.AuthorizationDecision; import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity; import org.springframework.security.config.web.server.HttpSecurity; import org.springframework.security.core.Authentication; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.web.server.authorization.AuthorizationContext; import org.springframework.web.server.WebFilter; import reactor.core.publisher.Mono; @@ -49,4 +52,12 @@ public class SecurityConfig { .map( a -> context.getVariables().get("user").equals(a.getName())) .map( granted -> new AuthorizationDecision(granted)); } + + @Bean + public MapUserDetailsRepository userDetailsRepository() { + UserDetails rob = User.withUsername("rob").password("rob").roles("USER").build(); + UserDetails admin = User.withUsername("admin").password("admin").roles("USER","ADMIN").build(); + return new MapUserDetailsRepository(rob, admin); + } + } diff --git a/samples/javaconfig/hellowebfluxfn/src/main/java/sample/User.java b/samples/javaconfig/hellowebfluxfn/src/main/java/sample/User.java deleted file mode 100644 index ef81772c6b..0000000000 --- a/samples/javaconfig/hellowebfluxfn/src/main/java/sample/User.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2002-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package sample; - -/** - * @author Rob Winch - * @since 5.0 - */ -public class User { - - private Long id; - private String username; - private String password; - private String firstname; - private String lastname; - - public User() {} - - public User(User copy) { - this(copy.getUsername(), copy.getPassword(), copy.getFirstname(), copy.getLastname()); - } - - public User(String username, String password, String firstname, String lastname) { - super(); - this.username = username; - this.password = password; - this.firstname = firstname; - this.lastname = lastname; - } - - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public String getUsername() { - return username; - } - - public void setUsername(String username) { - this.username = username; - } - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } - - public String getFirstname() { - return firstname; - } - - public void setFirstname(String firstname) { - this.firstname = firstname; - } - - public String getLastname() { - return lastname; - } - - public void setLastname(String lastname) { - this.lastname = lastname; - } -} diff --git a/samples/javaconfig/hellowebfluxfn/src/main/java/sample/UserController.java b/samples/javaconfig/hellowebfluxfn/src/main/java/sample/UserController.java index cadc1d6e22..62a64c502f 100644 --- a/samples/javaconfig/hellowebfluxfn/src/main/java/sample/UserController.java +++ b/samples/javaconfig/hellowebfluxfn/src/main/java/sample/UserController.java @@ -18,6 +18,7 @@ package sample; import org.springframework.http.MediaType; import org.springframework.security.core.Authentication; +import org.springframework.security.core.userdetails.User; import org.springframework.security.web.server.context.SecurityContextRepository; import org.springframework.security.web.server.context.WebSessionSecurityContextRepository; import org.springframework.stereotype.Component; @@ -37,11 +38,6 @@ import java.util.Map; public class UserController { private final SecurityContextRepository repo = new WebSessionSecurityContextRepository(); - private final UserRepository users; - - public UserController(UserRepository users) { - this.users = users; - } public Mono principal(ServerRequest serverRequest) { return serverRequest.principal().cast(Authentication.class).flatMap(p -> @@ -50,12 +46,6 @@ public class UserController { .syncBody(p.getPrincipal())); } - public Mono users(ServerRequest serverRequest) { - return ServerResponse.ok() - .contentType(MediaType.APPLICATION_JSON) - .body(this.users.findAll(), User.class); - } - public Mono admin(ServerRequest serverRequest) { return serverRequest.principal().cast(Authentication.class).flatMap(p -> ServerResponse.ok() diff --git a/samples/javaconfig/hellowebfluxfn/src/main/java/sample/UserRepository.java b/samples/javaconfig/hellowebfluxfn/src/main/java/sample/UserRepository.java deleted file mode 100644 index 58fd96b437..0000000000 --- a/samples/javaconfig/hellowebfluxfn/src/main/java/sample/UserRepository.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2002-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package sample; - -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -/** - * - * @author Rob Winch - * @since 5.0 - */ -public interface UserRepository { - - Flux findAll(); - - Mono findByUsername(String username); - - Mono save(User user); -} diff --git a/samples/javaconfig/hellowebfluxfn/src/main/java/sample/UserRepositoryUserDetailsRepository.java b/samples/javaconfig/hellowebfluxfn/src/main/java/sample/UserRepositoryUserDetailsRepository.java deleted file mode 100644 index d475e7b10b..0000000000 --- a/samples/javaconfig/hellowebfluxfn/src/main/java/sample/UserRepositoryUserDetailsRepository.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2002-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package sample; - -import java.util.Collection; -import java.util.List; - -import org.springframework.security.authentication.UserDetailsRepository; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.AuthorityUtils; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.stereotype.Component; - -import reactor.core.publisher.Mono; - -/** - * @author Rob Winch - * @since 5.0 - */ -@Component -public class UserRepositoryUserDetailsRepository implements UserDetailsRepository { - private final UserRepository users; - - public UserRepositoryUserDetailsRepository(UserRepository users) { - super(); - this.users = users; - } - - @Override - public Mono findByUsername(String username) { - return this.users - .findByUsername(username) - .map(UserDetailsAdapter::new); - } - - @SuppressWarnings("serial") - private static class UserDetailsAdapter extends User implements UserDetails { - private static List USER_ROLES = AuthorityUtils.createAuthorityList("ROLE_USER"); - private static List ADMIN_ROLES = AuthorityUtils.createAuthorityList("ROLE_ADMIN", "ROLE_USER"); - - private UserDetailsAdapter(User delegate) { - super(delegate); - } - - @Override - public Collection getAuthorities() { - return isAdmin() ? ADMIN_ROLES : USER_ROLES ; - } - - private boolean isAdmin() { - return getUsername().contains("admin"); - } - - @Override - public boolean isAccountNonExpired() { - return true; - } - - @Override - public boolean isAccountNonLocked() { - return true; - } - - @Override - public boolean isCredentialsNonExpired() { - return true; - } - - @Override - public boolean isEnabled() { - return true; - } - } -} diff --git a/samples/javaconfig/hellowebfluxfn/src/test/java/sample/HelloWebfluxFnApplicationTests.java b/samples/javaconfig/hellowebfluxfn/src/test/java/sample/HelloWebfluxFnApplicationTests.java index 77575d8540..f9e7641ee0 100644 --- a/samples/javaconfig/hellowebfluxfn/src/test/java/sample/HelloWebfluxFnApplicationTests.java +++ b/samples/javaconfig/hellowebfluxfn/src/test/java/sample/HelloWebfluxFnApplicationTests.java @@ -22,7 +22,6 @@ import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationContext; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseCookie; @@ -59,11 +58,12 @@ public class HelloWebfluxFnApplicationTests { this.rest = WebTestClient.bindToHttpHandler(handler).build(); } + @Test public void basicRequired() throws Exception { this.rest .get() - .uri("/users") + .uri("/principal") .exchange() .expectStatus().isUnauthorized(); } @@ -73,10 +73,10 @@ public class HelloWebfluxFnApplicationTests { this.rest .filter(robsCredentials()) .get() - .uri("/users") + .uri("/principal") .exchange() .expectStatus().isOk() - .expectBody().json("[{\"id\":null,\"username\":\"rob\",\"password\":\"rob\",\"firstname\":\"Rob\",\"lastname\":\"Winch\"},{\"id\":null,\"username\":\"admin\",\"password\":\"admin\",\"firstname\":\"Admin\",\"lastname\":\"User\"}]"); + .expectBody().json("{\"username\":\"rob\"}"); } @Test @@ -84,7 +84,7 @@ public class HelloWebfluxFnApplicationTests { this.rest .filter(invalidPassword()) .get() - .uri("/users") + .uri("/principal") .exchange() .expectStatus().isUnauthorized() .expectBody().isEmpty(); @@ -146,7 +146,7 @@ public class HelloWebfluxFnApplicationTests { ExchangeResult result = this.rest .filter(robsCredentials()) .get() - .uri("/users") + .uri("/principal") .exchange() .returnResult(String.class); @@ -154,7 +154,7 @@ public class HelloWebfluxFnApplicationTests { this.rest .get() - .uri("/users") + .uri("/principal") .cookie(session.getName(), session.getValue()) .exchange() .expectStatus().isOk(); @@ -166,13 +166,13 @@ public class HelloWebfluxFnApplicationTests { this.rest .exchangeMutator( withUser() ) .get() - .uri("/users") + .uri("/principal") .exchange() .expectStatus().isOk(); this.rest .get() - .uri("/users") + .uri("/principal") .exchange() .expectStatus().isUnauthorized(); }