From 2edc4c585a843d9fa0bc33145a45506d13d4c472 Mon Sep 17 00:00:00 2001 From: Andreas Marek Date: Tue, 16 Mar 2021 04:55:45 +1100 Subject: [PATCH 01/12] add employee service example --- samples/employee-service/build.gradle | 22 +++++ .../spring/sample/graphql/DataFetchers.java | 36 +++++++ .../io/spring/sample/graphql/Employee.java | 29 ++++++ .../sample/graphql/EmployeeService.java | 15 +++ .../graphql/EmployeeServiceApplication.java | 28 ++++++ .../spring/sample/graphql/GraphQLWiring.java | 33 +++++++ .../spring/sample/graphql/SalaryService.java | 23 +++++ .../spring/sample/graphql/SecurityConfig.java | 39 ++++++++ .../src/main/resources/application.properties | 7 ++ .../src/main/resources/schema.graphqls | 22 +++++ .../EmployeeServiceApplicationTest.java | 98 +++++++++++++++++++ settings.gradle | 3 +- 12 files changed, 354 insertions(+), 1 deletion(-) create mode 100644 samples/employee-service/build.gradle create mode 100644 samples/employee-service/src/main/java/io/spring/sample/graphql/DataFetchers.java create mode 100644 samples/employee-service/src/main/java/io/spring/sample/graphql/Employee.java create mode 100644 samples/employee-service/src/main/java/io/spring/sample/graphql/EmployeeService.java create mode 100644 samples/employee-service/src/main/java/io/spring/sample/graphql/EmployeeServiceApplication.java create mode 100644 samples/employee-service/src/main/java/io/spring/sample/graphql/GraphQLWiring.java create mode 100644 samples/employee-service/src/main/java/io/spring/sample/graphql/SalaryService.java create mode 100644 samples/employee-service/src/main/java/io/spring/sample/graphql/SecurityConfig.java create mode 100644 samples/employee-service/src/main/resources/application.properties create mode 100644 samples/employee-service/src/main/resources/schema.graphqls create mode 100644 samples/employee-service/src/test/java/io/spring/sample/graphql/EmployeeServiceApplicationTest.java diff --git a/samples/employee-service/build.gradle b/samples/employee-service/build.gradle new file mode 100644 index 00000000..46f42657 --- /dev/null +++ b/samples/employee-service/build.gradle @@ -0,0 +1,22 @@ +plugins { + id 'org.springframework.boot' version '2.4.1' + id 'io.spring.dependency-management' version '1.0.10.RELEASE' + id 'java' +} +group = 'com.example' +version = '0.0.1-SNAPSHOT' +description = "GraphQL webflux security example" +sourceCompatibility = '1.8' + +dependencies { + implementation project(':graphql-spring-boot-starter') + implementation 'org.springframework.boot:spring-boot-starter-webflux' + implementation 'org.springframework.boot:spring-boot-starter-security' + implementation 'org.springframework.boot:spring-boot-starter-actuator' + developmentOnly 'org.springframework.boot:spring-boot-devtools' + testImplementation 'org.springframework.boot:spring-boot-starter-test' + testImplementation 'org.springframework.security:spring-security-test' +} +test { + useJUnitPlatform() +} \ No newline at end of file diff --git a/samples/employee-service/src/main/java/io/spring/sample/graphql/DataFetchers.java b/samples/employee-service/src/main/java/io/spring/sample/graphql/DataFetchers.java new file mode 100644 index 00000000..19e1817b --- /dev/null +++ b/samples/employee-service/src/main/java/io/spring/sample/graphql/DataFetchers.java @@ -0,0 +1,36 @@ +package io.spring.sample.graphql; + +import graphql.schema.DataFetcher; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.math.BigDecimal; +import java.util.Map; + +@Component +public class DataFetchers { + + @Autowired + EmployeeService employeeService; + + @Autowired + SalaryService salaryService; + + public DataFetcher employeeDataFetcher = env -> { + return employeeService.getAllEmployees(); + }; + + public DataFetcher salaryDataFetcher = env -> { + Employee employee = env.getSource(); + return salaryService.getSalaryForEmployee(employee).toFuture(); + }; + + public DataFetcher updateSalaryFetcher = env -> { + Map input = env.getArgument("input"); + String employeeId = input.get("employeeId"); + BigDecimal newSalary = new BigDecimal(input.get("salary")); + salaryService.updateSalary(employeeId, newSalary); + return null; + }; + +} diff --git a/samples/employee-service/src/main/java/io/spring/sample/graphql/Employee.java b/samples/employee-service/src/main/java/io/spring/sample/graphql/Employee.java new file mode 100644 index 00000000..e2bc9b69 --- /dev/null +++ b/samples/employee-service/src/main/java/io/spring/sample/graphql/Employee.java @@ -0,0 +1,29 @@ +package io.spring.sample.graphql; + +public class Employee { + + private String id; + private String name; + + public Employee(String id, String name) { + this.id = id; + this.name = name; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + +} diff --git a/samples/employee-service/src/main/java/io/spring/sample/graphql/EmployeeService.java b/samples/employee-service/src/main/java/io/spring/sample/graphql/EmployeeService.java new file mode 100644 index 00000000..8d64597c --- /dev/null +++ b/samples/employee-service/src/main/java/io/spring/sample/graphql/EmployeeService.java @@ -0,0 +1,15 @@ +package io.spring.sample.graphql; + +import org.springframework.stereotype.Component; + +import java.util.Arrays; +import java.util.List; + +@Component +public class EmployeeService { + + public List getAllEmployees() { + return Arrays.asList(new Employee("1", "Andi")); + } + +} diff --git a/samples/employee-service/src/main/java/io/spring/sample/graphql/EmployeeServiceApplication.java b/samples/employee-service/src/main/java/io/spring/sample/graphql/EmployeeServiceApplication.java new file mode 100644 index 00000000..2538c875 --- /dev/null +++ b/samples/employee-service/src/main/java/io/spring/sample/graphql/EmployeeServiceApplication.java @@ -0,0 +1,28 @@ +/* + * Copyright 2002-2020 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 + * + * https://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 io.spring.sample.graphql; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class EmployeeServiceApplication { + + public static void main(String[] args) { + SpringApplication.run(EmployeeServiceApplication.class, args); + } +} diff --git a/samples/employee-service/src/main/java/io/spring/sample/graphql/GraphQLWiring.java b/samples/employee-service/src/main/java/io/spring/sample/graphql/GraphQLWiring.java new file mode 100644 index 00000000..22f6f5e1 --- /dev/null +++ b/samples/employee-service/src/main/java/io/spring/sample/graphql/GraphQLWiring.java @@ -0,0 +1,33 @@ +package io.spring.sample.graphql; + +import graphql.schema.idl.RuntimeWiring; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.graphql.boot.RuntimeWiringCustomizer; +import org.springframework.stereotype.Component; + +@Component +public class GraphQLWiring implements RuntimeWiringCustomizer { + + @Autowired + DataFetchers dataFetchers; + + @Override + public void customize(RuntimeWiring.Builder builder) { + builder.type("Query", + wiringBuilder -> { + return wiringBuilder. + dataFetcher("employees", dataFetchers.employeeDataFetcher); + }); + builder.type("Employee", + wiringBuilder -> { + return wiringBuilder. + dataFetcher("salary", dataFetchers.salaryDataFetcher); + }); + builder.type("Mutation", + wiringBuilder -> { + return wiringBuilder. + dataFetcher("updateSalary", dataFetchers.updateSalaryFetcher); + }); + + } +} diff --git a/samples/employee-service/src/main/java/io/spring/sample/graphql/SalaryService.java b/samples/employee-service/src/main/java/io/spring/sample/graphql/SalaryService.java new file mode 100644 index 00000000..5a17b1b9 --- /dev/null +++ b/samples/employee-service/src/main/java/io/spring/sample/graphql/SalaryService.java @@ -0,0 +1,23 @@ +package io.spring.sample.graphql; + +import org.springframework.security.access.annotation.Secured; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.stereotype.Component; +import reactor.core.publisher.Mono; + +import java.math.BigDecimal; + +@Component +public class SalaryService { + + + @PreAuthorize("hasRole('MANAGER')") + public Mono getSalaryForEmployee(Employee employee) { + return Mono.just(new BigDecimal("42")); + } + + @Secured({"ROLE_HR"}) + public void updateSalary(String employeeId, BigDecimal newSalary) { + + } +} diff --git a/samples/employee-service/src/main/java/io/spring/sample/graphql/SecurityConfig.java b/samples/employee-service/src/main/java/io/spring/sample/graphql/SecurityConfig.java new file mode 100644 index 00000000..1794b022 --- /dev/null +++ b/samples/employee-service/src/main/java/io/spring/sample/graphql/SecurityConfig.java @@ -0,0 +1,39 @@ +package io.spring.sample.graphql; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity; +import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity; +import org.springframework.security.config.web.server.ServerHttpSecurity; +import org.springframework.security.core.userdetails.MapReactiveUserDetailsService; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.web.server.SecurityWebFilterChain; + +@Configuration +@EnableWebFluxSecurity +@EnableReactiveMethodSecurity +public class SecurityConfig { + + @Bean + SecurityWebFilterChain springWebFilterChain(ServerHttpSecurity http) throws Exception { + http.csrf().disable(); + return http + // Demonstrate that method security works + // Best practice to use both for defense in depth + .authorizeExchange() + .anyExchange().permitAll() + .and() + .httpBasic().and() + .build(); + } + + @Bean + public MapReactiveUserDetailsService userDetailsService() { + User.UserBuilder userBuilder = User.withDefaultPasswordEncoder(); + UserDetails rob = userBuilder.username("rob").password("rob").roles("USER").build(); + UserDetails admin = userBuilder.username("admin").password("admin").roles("USER", "ADMIN").build(); + return new MapReactiveUserDetailsService(rob, admin); + } + +} diff --git a/samples/employee-service/src/main/resources/application.properties b/samples/employee-service/src/main/resources/application.properties new file mode 100644 index 00000000..cc94084b --- /dev/null +++ b/samples/employee-service/src/main/resources/application.properties @@ -0,0 +1,7 @@ +spring.graphql.websocket.path=/graphql +management.endpoints.web.exposure.include=health,metrics,info +logging.level.org.springframework.web=debug +logging.level.org.springframework.http=debug +logging.level.org.springframework.graphql=debug +logging.level.org.springframework.security=debug +logging.level.reactor.netty=debug \ No newline at end of file diff --git a/samples/employee-service/src/main/resources/schema.graphqls b/samples/employee-service/src/main/resources/schema.graphqls new file mode 100644 index 00000000..7687c854 --- /dev/null +++ b/samples/employee-service/src/main/resources/schema.graphqls @@ -0,0 +1,22 @@ +type Query { + employees: [Employee] +} +type Mutation { + # restricted + updateSalary(input: UpdateSalaryInput!): UpdateSalaryPayload +} +type Employee { + id: ID! + name: String + # restricted + salary: String +} + +input UpdateSalaryInput { + employeeId: ID! + salary: String! +} +type UpdateSalaryPayload { + success: Boolean! + employee: Employee +} diff --git a/samples/employee-service/src/test/java/io/spring/sample/graphql/EmployeeServiceApplicationTest.java b/samples/employee-service/src/test/java/io/spring/sample/graphql/EmployeeServiceApplicationTest.java new file mode 100644 index 00000000..b2d1c499 --- /dev/null +++ b/samples/employee-service/src/test/java/io/spring/sample/graphql/EmployeeServiceApplicationTest.java @@ -0,0 +1,98 @@ +package io.spring.sample.graphql; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext; +import org.springframework.http.MediaType; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers; +import org.springframework.test.web.reactive.server.WebTestClient; +import org.springframework.web.reactive.function.client.ExchangeFilterFunctions; + +import java.util.Collections; + +@SpringBootTest() +class EmployeeServiceApplicationTest { + + @Autowired + private ReactiveWebApplicationContext context; + private static final String BASE_URL = "https://spring.example.org/graphql"; + + + WebTestClient client; + + @BeforeEach + public void setup() { + this.client = WebTestClient + .bindToApplicationContext(this.context) + .apply(SecurityMockServerConfigurers.springSecurity()) + .configureClient() + .filter(ExchangeFilterFunctions.basicAuthentication()) + .defaultHeaders(headers -> { + headers.setContentType(MediaType.APPLICATION_JSON); + headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); + }) + .baseUrl(BASE_URL) + .build(); + } + + @Test + void canQueryName() { + String query = "{" + + " employees{ " + + " name" + + " }" + + "}"; + + + client.post().uri("") + .bodyValue("{ \"query\": \"" + query + "\"}") + .exchange() + .expectStatus().isOk() + .expectBody().jsonPath("data.employees[0].name").isEqualTo("Andi"); + + } + + @Test + void canNotQuerySalary() { + String query = "{" + + " employees{ " + + " name" + + " salary" + + " }" + + "}"; + + + client.post().uri("") + .bodyValue("{ \"query\": \"" + query + "\"}") + .exchange() + .expectStatus().isOk() + .expectBody() + .jsonPath("data.employees[0].name").isEqualTo("Andi") + .jsonPath("data.employees[0].salary").doesNotExist(); + + } + + @Test + @WithMockUser(roles = "MANAGER") + void canQuerySalaryAsManager() { + String query = "{" + + " employees{ " + + " name" + + " salary" + + " }" + + "}"; + + + client.post().uri("") + .bodyValue("{ \"query\": \"" + query + "\"}") + .exchange() + .expectStatus().isOk() + .expectBody() + .jsonPath("data.employees[0].name").isEqualTo("Andi") + .jsonPath("data.employees[0].salary").isEqualTo("42"); + + } +} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index c1194735..1bd1ed3b 100644 --- a/settings.gradle +++ b/settings.gradle @@ -19,4 +19,5 @@ include 'spring-graphql', 'spring-graphql-test', 'graphql-spring-boot-starter', 'samples:webmvc-http', - 'samples:webflux-websocket' + 'samples:webflux-websocket', + 'samples:employee-service' From 4b44f7a9870e3140e353841f736e16fabd3236f6 Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Wed, 28 Apr 2021 11:39:47 -0500 Subject: [PATCH 02/12] Use ADMIN role --- .../main/java/io/spring/sample/graphql/SalaryService.java | 2 +- .../sample/graphql/EmployeeServiceApplicationTest.java | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/samples/employee-service/src/main/java/io/spring/sample/graphql/SalaryService.java b/samples/employee-service/src/main/java/io/spring/sample/graphql/SalaryService.java index 5a17b1b9..6d52b2e4 100644 --- a/samples/employee-service/src/main/java/io/spring/sample/graphql/SalaryService.java +++ b/samples/employee-service/src/main/java/io/spring/sample/graphql/SalaryService.java @@ -11,7 +11,7 @@ import java.math.BigDecimal; public class SalaryService { - @PreAuthorize("hasRole('MANAGER')") + @PreAuthorize("hasRole('ADMIN')") public Mono getSalaryForEmployee(Employee employee) { return Mono.just(new BigDecimal("42")); } diff --git a/samples/employee-service/src/test/java/io/spring/sample/graphql/EmployeeServiceApplicationTest.java b/samples/employee-service/src/test/java/io/spring/sample/graphql/EmployeeServiceApplicationTest.java index b2d1c499..c129f51f 100644 --- a/samples/employee-service/src/test/java/io/spring/sample/graphql/EmployeeServiceApplicationTest.java +++ b/samples/employee-service/src/test/java/io/spring/sample/graphql/EmployeeServiceApplicationTest.java @@ -13,6 +13,8 @@ import org.springframework.web.reactive.function.client.ExchangeFilterFunctions; import java.util.Collections; +import static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.mockUser; + @SpringBootTest() class EmployeeServiceApplicationTest { @@ -76,8 +78,8 @@ class EmployeeServiceApplicationTest { } @Test - @WithMockUser(roles = "MANAGER") - void canQuerySalaryAsManager() { + @WithMockUser("hasRole('ADMIN')") + void canQuerySalaryAsAdmin() { String query = "{" + " employees{ " + " name" + From 84b9b65777d9b5ba1faa44171ca66877540e6809 Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Wed, 28 Apr 2021 11:41:02 -0500 Subject: [PATCH 03/12] Use headers instead of WithMockUser This ensures that we don't get false positives when the Reactor return types are not continuous. --- .../sample/graphql/EmployeeServiceApplicationTest.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/samples/employee-service/src/test/java/io/spring/sample/graphql/EmployeeServiceApplicationTest.java b/samples/employee-service/src/test/java/io/spring/sample/graphql/EmployeeServiceApplicationTest.java index c129f51f..54a32006 100644 --- a/samples/employee-service/src/test/java/io/spring/sample/graphql/EmployeeServiceApplicationTest.java +++ b/samples/employee-service/src/test/java/io/spring/sample/graphql/EmployeeServiceApplicationTest.java @@ -6,15 +6,12 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext; import org.springframework.http.MediaType; -import org.springframework.security.test.context.support.WithMockUser; import org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers; import org.springframework.test.web.reactive.server.WebTestClient; import org.springframework.web.reactive.function.client.ExchangeFilterFunctions; import java.util.Collections; -import static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.mockUser; - @SpringBootTest() class EmployeeServiceApplicationTest { @@ -78,7 +75,6 @@ class EmployeeServiceApplicationTest { } @Test - @WithMockUser("hasRole('ADMIN')") void canQuerySalaryAsAdmin() { String query = "{" + " employees{ " + @@ -89,6 +85,7 @@ class EmployeeServiceApplicationTest { client.post().uri("") + .headers(h -> h.setBasicAuth("admin", "admin")) .bodyValue("{ \"query\": \"" + query + "\"}") .exchange() .expectStatus().isOk() From ca369ecc7c1f70848689d41b04ea3b7b272ac85c Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Wed, 28 Apr 2021 11:41:35 -0500 Subject: [PATCH 04/12] salaryDataFetcher returns Mono We need the return type to be a Reactor type for the context to be propagated. --- .../src/main/java/io/spring/sample/graphql/DataFetchers.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/employee-service/src/main/java/io/spring/sample/graphql/DataFetchers.java b/samples/employee-service/src/main/java/io/spring/sample/graphql/DataFetchers.java index 19e1817b..6a2e2c6a 100644 --- a/samples/employee-service/src/main/java/io/spring/sample/graphql/DataFetchers.java +++ b/samples/employee-service/src/main/java/io/spring/sample/graphql/DataFetchers.java @@ -22,7 +22,7 @@ public class DataFetchers { public DataFetcher salaryDataFetcher = env -> { Employee employee = env.getSource(); - return salaryService.getSalaryForEmployee(employee).toFuture(); + return salaryService.getSalaryForEmployee(employee); }; public DataFetcher updateSalaryFetcher = env -> { From 9b4c053a3038d8e09feb8bc63137273278f0c77a Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Fri, 7 May 2021 13:58:56 -0500 Subject: [PATCH 05/12] invalidCredentials --- .../EmployeeServiceApplicationTest.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/samples/employee-service/src/test/java/io/spring/sample/graphql/EmployeeServiceApplicationTest.java b/samples/employee-service/src/test/java/io/spring/sample/graphql/EmployeeServiceApplicationTest.java index 54a32006..e37caa49 100644 --- a/samples/employee-service/src/test/java/io/spring/sample/graphql/EmployeeServiceApplicationTest.java +++ b/samples/employee-service/src/test/java/io/spring/sample/graphql/EmployeeServiceApplicationTest.java @@ -94,4 +94,24 @@ class EmployeeServiceApplicationTest { .jsonPath("data.employees[0].salary").isEqualTo("42"); } + + @Test + void invalidCredentials() { + String query = "{" + + " employees{ " + + " name" + + " salary" + + " }" + + "}"; + + + client.post().uri("") + .headers(h -> h.setBasicAuth("admin", "INVALID")) + .bodyValue("{ \"query\": \"" + query + "\"}") + .exchange() + .expectStatus().isUnauthorized() + .expectBody() + .isEmpty(); + + } } \ No newline at end of file From d3a0fa1c4f7bd9452ce6decd095b13a8511836c9 Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Fri, 14 May 2021 14:04:31 -0500 Subject: [PATCH 06/12] schema.graphqls->/graphql/schema.graphqls --- .../src/main/resources/{ => graphql}/schema.graphqls | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename samples/employee-service/src/main/resources/{ => graphql}/schema.graphqls (100%) diff --git a/samples/employee-service/src/main/resources/schema.graphqls b/samples/employee-service/src/main/resources/graphql/schema.graphqls similarity index 100% rename from samples/employee-service/src/main/resources/schema.graphqls rename to samples/employee-service/src/main/resources/graphql/schema.graphqls From 05098fd5d1e6de5e29aafe0bf18cf851299d282c Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Wed, 2 Jun 2021 13:29:22 -0500 Subject: [PATCH 07/12] Update to Spring Boot 2.5.0 --- samples/employee-service/build.gradle | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/samples/employee-service/build.gradle b/samples/employee-service/build.gradle index 46f42657..493aea7d 100644 --- a/samples/employee-service/build.gradle +++ b/samples/employee-service/build.gradle @@ -1,6 +1,5 @@ plugins { - id 'org.springframework.boot' version '2.4.1' - id 'io.spring.dependency-management' version '1.0.10.RELEASE' + id 'org.springframework.boot' version '2.5.0' id 'java' } group = 'com.example' From 96d25b2a911c617e5a6f09ed8f76200c784e943c Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Wed, 2 Jun 2021 13:36:40 -0500 Subject: [PATCH 08/12] employee-service -> webflux-security --- samples/{employee-service => webflux-security}/build.gradle | 0 .../src/main/java/io/spring/sample/graphql/DataFetchers.java | 0 .../src/main/java/io/spring/sample/graphql/Employee.java | 0 .../main/java/io/spring/sample/graphql/EmployeeService.java | 0 .../io/spring/sample/graphql/EmployeeServiceApplication.java | 0 .../src/main/java/io/spring/sample/graphql/GraphQLWiring.java | 0 .../src/main/java/io/spring/sample/graphql/SalaryService.java | 0 .../main/java/io/spring/sample/graphql/SecurityConfig.java | 0 .../src/main/resources/application.properties | 0 .../src/main/resources/graphql/schema.graphqls | 0 .../spring/sample/graphql/EmployeeServiceApplicationTest.java | 0 settings.gradle | 4 ++-- 12 files changed, 2 insertions(+), 2 deletions(-) rename samples/{employee-service => webflux-security}/build.gradle (100%) rename samples/{employee-service => webflux-security}/src/main/java/io/spring/sample/graphql/DataFetchers.java (100%) rename samples/{employee-service => webflux-security}/src/main/java/io/spring/sample/graphql/Employee.java (100%) rename samples/{employee-service => webflux-security}/src/main/java/io/spring/sample/graphql/EmployeeService.java (100%) rename samples/{employee-service => webflux-security}/src/main/java/io/spring/sample/graphql/EmployeeServiceApplication.java (100%) rename samples/{employee-service => webflux-security}/src/main/java/io/spring/sample/graphql/GraphQLWiring.java (100%) rename samples/{employee-service => webflux-security}/src/main/java/io/spring/sample/graphql/SalaryService.java (100%) rename samples/{employee-service => webflux-security}/src/main/java/io/spring/sample/graphql/SecurityConfig.java (100%) rename samples/{employee-service => webflux-security}/src/main/resources/application.properties (100%) rename samples/{employee-service => webflux-security}/src/main/resources/graphql/schema.graphqls (100%) rename samples/{employee-service => webflux-security}/src/test/java/io/spring/sample/graphql/EmployeeServiceApplicationTest.java (100%) diff --git a/samples/employee-service/build.gradle b/samples/webflux-security/build.gradle similarity index 100% rename from samples/employee-service/build.gradle rename to samples/webflux-security/build.gradle diff --git a/samples/employee-service/src/main/java/io/spring/sample/graphql/DataFetchers.java b/samples/webflux-security/src/main/java/io/spring/sample/graphql/DataFetchers.java similarity index 100% rename from samples/employee-service/src/main/java/io/spring/sample/graphql/DataFetchers.java rename to samples/webflux-security/src/main/java/io/spring/sample/graphql/DataFetchers.java diff --git a/samples/employee-service/src/main/java/io/spring/sample/graphql/Employee.java b/samples/webflux-security/src/main/java/io/spring/sample/graphql/Employee.java similarity index 100% rename from samples/employee-service/src/main/java/io/spring/sample/graphql/Employee.java rename to samples/webflux-security/src/main/java/io/spring/sample/graphql/Employee.java diff --git a/samples/employee-service/src/main/java/io/spring/sample/graphql/EmployeeService.java b/samples/webflux-security/src/main/java/io/spring/sample/graphql/EmployeeService.java similarity index 100% rename from samples/employee-service/src/main/java/io/spring/sample/graphql/EmployeeService.java rename to samples/webflux-security/src/main/java/io/spring/sample/graphql/EmployeeService.java diff --git a/samples/employee-service/src/main/java/io/spring/sample/graphql/EmployeeServiceApplication.java b/samples/webflux-security/src/main/java/io/spring/sample/graphql/EmployeeServiceApplication.java similarity index 100% rename from samples/employee-service/src/main/java/io/spring/sample/graphql/EmployeeServiceApplication.java rename to samples/webflux-security/src/main/java/io/spring/sample/graphql/EmployeeServiceApplication.java diff --git a/samples/employee-service/src/main/java/io/spring/sample/graphql/GraphQLWiring.java b/samples/webflux-security/src/main/java/io/spring/sample/graphql/GraphQLWiring.java similarity index 100% rename from samples/employee-service/src/main/java/io/spring/sample/graphql/GraphQLWiring.java rename to samples/webflux-security/src/main/java/io/spring/sample/graphql/GraphQLWiring.java diff --git a/samples/employee-service/src/main/java/io/spring/sample/graphql/SalaryService.java b/samples/webflux-security/src/main/java/io/spring/sample/graphql/SalaryService.java similarity index 100% rename from samples/employee-service/src/main/java/io/spring/sample/graphql/SalaryService.java rename to samples/webflux-security/src/main/java/io/spring/sample/graphql/SalaryService.java diff --git a/samples/employee-service/src/main/java/io/spring/sample/graphql/SecurityConfig.java b/samples/webflux-security/src/main/java/io/spring/sample/graphql/SecurityConfig.java similarity index 100% rename from samples/employee-service/src/main/java/io/spring/sample/graphql/SecurityConfig.java rename to samples/webflux-security/src/main/java/io/spring/sample/graphql/SecurityConfig.java diff --git a/samples/employee-service/src/main/resources/application.properties b/samples/webflux-security/src/main/resources/application.properties similarity index 100% rename from samples/employee-service/src/main/resources/application.properties rename to samples/webflux-security/src/main/resources/application.properties diff --git a/samples/employee-service/src/main/resources/graphql/schema.graphqls b/samples/webflux-security/src/main/resources/graphql/schema.graphqls similarity index 100% rename from samples/employee-service/src/main/resources/graphql/schema.graphqls rename to samples/webflux-security/src/main/resources/graphql/schema.graphqls diff --git a/samples/employee-service/src/test/java/io/spring/sample/graphql/EmployeeServiceApplicationTest.java b/samples/webflux-security/src/test/java/io/spring/sample/graphql/EmployeeServiceApplicationTest.java similarity index 100% rename from samples/employee-service/src/test/java/io/spring/sample/graphql/EmployeeServiceApplicationTest.java rename to samples/webflux-security/src/test/java/io/spring/sample/graphql/EmployeeServiceApplicationTest.java diff --git a/settings.gradle b/settings.gradle index 1bd1ed3b..6df3f2fd 100644 --- a/settings.gradle +++ b/settings.gradle @@ -19,5 +19,5 @@ include 'spring-graphql', 'spring-graphql-test', 'graphql-spring-boot-starter', 'samples:webmvc-http', - 'samples:webflux-websocket', - 'samples:employee-service' + 'samples:webflux-security', + 'samples:webflux-websocket' From 16f2baa263820f3672278044cec2aa174e5faacb Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Wed, 2 Jun 2021 13:55:02 -0500 Subject: [PATCH 09/12] Format webflux-security --- .../spring/sample/graphql/DataFetchers.java | 45 ++--- .../io/spring/sample/graphql/Employee.java | 37 ++-- .../sample/graphql/EmployeeService.java | 10 +- .../graphql/EmployeeServiceApplication.java | 6 +- .../spring/sample/graphql/GraphQLWiring.java | 41 ++--- .../spring/sample/graphql/SalaryService.java | 21 +-- .../spring/sample/graphql/SecurityConfig.java | 38 ++--- .../EmployeeServiceApplicationTest.java | 160 +++++++++--------- 8 files changed, 181 insertions(+), 177 deletions(-) diff --git a/samples/webflux-security/src/main/java/io/spring/sample/graphql/DataFetchers.java b/samples/webflux-security/src/main/java/io/spring/sample/graphql/DataFetchers.java index 6a2e2c6a..530cc9da 100644 --- a/samples/webflux-security/src/main/java/io/spring/sample/graphql/DataFetchers.java +++ b/samples/webflux-security/src/main/java/io/spring/sample/graphql/DataFetchers.java @@ -1,36 +1,37 @@ package io.spring.sample.graphql; -import graphql.schema.DataFetcher; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - import java.math.BigDecimal; import java.util.Map; +import graphql.schema.DataFetcher; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + @Component public class DataFetchers { - @Autowired - EmployeeService employeeService; + @Autowired + EmployeeService employeeService; - @Autowired - SalaryService salaryService; + public DataFetcher employeeDataFetcher = env -> { + return employeeService.getAllEmployees(); + }; - public DataFetcher employeeDataFetcher = env -> { - return employeeService.getAllEmployees(); - }; + @Autowired + SalaryService salaryService; - public DataFetcher salaryDataFetcher = env -> { - Employee employee = env.getSource(); - return salaryService.getSalaryForEmployee(employee); - }; + public DataFetcher salaryDataFetcher = env -> { + Employee employee = env.getSource(); + return salaryService.getSalaryForEmployee(employee); + }; - public DataFetcher updateSalaryFetcher = env -> { - Map input = env.getArgument("input"); - String employeeId = input.get("employeeId"); - BigDecimal newSalary = new BigDecimal(input.get("salary")); - salaryService.updateSalary(employeeId, newSalary); - return null; - }; + public DataFetcher updateSalaryFetcher = env -> { + Map input = env.getArgument("input"); + String employeeId = input.get("employeeId"); + BigDecimal newSalary = new BigDecimal(input.get("salary")); + salaryService.updateSalary(employeeId, newSalary); + return null; + }; } diff --git a/samples/webflux-security/src/main/java/io/spring/sample/graphql/Employee.java b/samples/webflux-security/src/main/java/io/spring/sample/graphql/Employee.java index e2bc9b69..0737dada 100644 --- a/samples/webflux-security/src/main/java/io/spring/sample/graphql/Employee.java +++ b/samples/webflux-security/src/main/java/io/spring/sample/graphql/Employee.java @@ -2,28 +2,29 @@ package io.spring.sample.graphql; public class Employee { - private String id; - private String name; + private String id; - public Employee(String id, String name) { - this.id = id; - this.name = name; - } + private String name; - public String getId() { - return id; - } + public Employee(String id, String name) { + this.id = id; + this.name = name; + } - public void setId(String id) { - this.id = id; - } + public String getId() { + return id; + } - public String getName() { - return name; - } + public void setId(String id) { + this.id = id; + } - public void setName(String name) { - this.name = name; - } + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } } diff --git a/samples/webflux-security/src/main/java/io/spring/sample/graphql/EmployeeService.java b/samples/webflux-security/src/main/java/io/spring/sample/graphql/EmployeeService.java index 8d64597c..b131f583 100644 --- a/samples/webflux-security/src/main/java/io/spring/sample/graphql/EmployeeService.java +++ b/samples/webflux-security/src/main/java/io/spring/sample/graphql/EmployeeService.java @@ -1,15 +1,15 @@ package io.spring.sample.graphql; -import org.springframework.stereotype.Component; - import java.util.Arrays; import java.util.List; +import org.springframework.stereotype.Component; + @Component public class EmployeeService { - public List getAllEmployees() { - return Arrays.asList(new Employee("1", "Andi")); - } + public List getAllEmployees() { + return Arrays.asList(new Employee("1", "Andi")); + } } diff --git a/samples/webflux-security/src/main/java/io/spring/sample/graphql/EmployeeServiceApplication.java b/samples/webflux-security/src/main/java/io/spring/sample/graphql/EmployeeServiceApplication.java index 2538c875..a8678a85 100644 --- a/samples/webflux-security/src/main/java/io/spring/sample/graphql/EmployeeServiceApplication.java +++ b/samples/webflux-security/src/main/java/io/spring/sample/graphql/EmployeeServiceApplication.java @@ -22,7 +22,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class EmployeeServiceApplication { - public static void main(String[] args) { - SpringApplication.run(EmployeeServiceApplication.class, args); - } + public static void main(String[] args) { + SpringApplication.run(EmployeeServiceApplication.class, args); + } } diff --git a/samples/webflux-security/src/main/java/io/spring/sample/graphql/GraphQLWiring.java b/samples/webflux-security/src/main/java/io/spring/sample/graphql/GraphQLWiring.java index 22f6f5e1..c162c86f 100644 --- a/samples/webflux-security/src/main/java/io/spring/sample/graphql/GraphQLWiring.java +++ b/samples/webflux-security/src/main/java/io/spring/sample/graphql/GraphQLWiring.java @@ -1,6 +1,7 @@ package io.spring.sample.graphql; import graphql.schema.idl.RuntimeWiring; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.graphql.boot.RuntimeWiringCustomizer; import org.springframework.stereotype.Component; @@ -8,26 +9,26 @@ import org.springframework.stereotype.Component; @Component public class GraphQLWiring implements RuntimeWiringCustomizer { - @Autowired - DataFetchers dataFetchers; + @Autowired + DataFetchers dataFetchers; - @Override - public void customize(RuntimeWiring.Builder builder) { - builder.type("Query", - wiringBuilder -> { - return wiringBuilder. - dataFetcher("employees", dataFetchers.employeeDataFetcher); - }); - builder.type("Employee", - wiringBuilder -> { - return wiringBuilder. - dataFetcher("salary", dataFetchers.salaryDataFetcher); - }); - builder.type("Mutation", - wiringBuilder -> { - return wiringBuilder. - dataFetcher("updateSalary", dataFetchers.updateSalaryFetcher); - }); + @Override + public void customize(RuntimeWiring.Builder builder) { + builder.type("Query", + wiringBuilder -> { + return wiringBuilder. + dataFetcher("employees", dataFetchers.employeeDataFetcher); + }); + builder.type("Employee", + wiringBuilder -> { + return wiringBuilder. + dataFetcher("salary", dataFetchers.salaryDataFetcher); + }); + builder.type("Mutation", + wiringBuilder -> { + return wiringBuilder. + dataFetcher("updateSalary", dataFetchers.updateSalaryFetcher); + }); - } + } } diff --git a/samples/webflux-security/src/main/java/io/spring/sample/graphql/SalaryService.java b/samples/webflux-security/src/main/java/io/spring/sample/graphql/SalaryService.java index 6d52b2e4..7cd9fb36 100644 --- a/samples/webflux-security/src/main/java/io/spring/sample/graphql/SalaryService.java +++ b/samples/webflux-security/src/main/java/io/spring/sample/graphql/SalaryService.java @@ -1,23 +1,24 @@ package io.spring.sample.graphql; +import java.math.BigDecimal; + +import reactor.core.publisher.Mono; + import org.springframework.security.access.annotation.Secured; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.stereotype.Component; -import reactor.core.publisher.Mono; - -import java.math.BigDecimal; @Component public class SalaryService { - @PreAuthorize("hasRole('ADMIN')") - public Mono getSalaryForEmployee(Employee employee) { - return Mono.just(new BigDecimal("42")); - } + @PreAuthorize("hasRole('ADMIN')") + public Mono getSalaryForEmployee(Employee employee) { + return Mono.just(new BigDecimal("42")); + } - @Secured({"ROLE_HR"}) - public void updateSalary(String employeeId, BigDecimal newSalary) { + @Secured({ "ROLE_HR" }) + public void updateSalary(String employeeId, BigDecimal newSalary) { - } + } } diff --git a/samples/webflux-security/src/main/java/io/spring/sample/graphql/SecurityConfig.java b/samples/webflux-security/src/main/java/io/spring/sample/graphql/SecurityConfig.java index 1794b022..d0a139f5 100644 --- a/samples/webflux-security/src/main/java/io/spring/sample/graphql/SecurityConfig.java +++ b/samples/webflux-security/src/main/java/io/spring/sample/graphql/SecurityConfig.java @@ -15,25 +15,25 @@ import org.springframework.security.web.server.SecurityWebFilterChain; @EnableReactiveMethodSecurity public class SecurityConfig { - @Bean - SecurityWebFilterChain springWebFilterChain(ServerHttpSecurity http) throws Exception { - http.csrf().disable(); - return http - // Demonstrate that method security works - // Best practice to use both for defense in depth - .authorizeExchange() - .anyExchange().permitAll() - .and() - .httpBasic().and() - .build(); - } + @Bean + SecurityWebFilterChain springWebFilterChain(ServerHttpSecurity http) throws Exception { + http.csrf().disable(); + return http + // Demonstrate that method security works + // Best practice to use both for defense in depth + .authorizeExchange() + .anyExchange().permitAll() + .and() + .httpBasic().and() + .build(); + } - @Bean - public MapReactiveUserDetailsService userDetailsService() { - User.UserBuilder userBuilder = User.withDefaultPasswordEncoder(); - UserDetails rob = userBuilder.username("rob").password("rob").roles("USER").build(); - UserDetails admin = userBuilder.username("admin").password("admin").roles("USER", "ADMIN").build(); - return new MapReactiveUserDetailsService(rob, admin); - } + @Bean + public MapReactiveUserDetailsService userDetailsService() { + User.UserBuilder userBuilder = User.withDefaultPasswordEncoder(); + UserDetails rob = userBuilder.username("rob").password("rob").roles("USER").build(); + UserDetails admin = userBuilder.username("admin").password("admin").roles("USER", "ADMIN").build(); + return new MapReactiveUserDetailsService(rob, admin); + } } diff --git a/samples/webflux-security/src/test/java/io/spring/sample/graphql/EmployeeServiceApplicationTest.java b/samples/webflux-security/src/test/java/io/spring/sample/graphql/EmployeeServiceApplicationTest.java index e37caa49..bb1a4bc9 100644 --- a/samples/webflux-security/src/test/java/io/spring/sample/graphql/EmployeeServiceApplicationTest.java +++ b/samples/webflux-security/src/test/java/io/spring/sample/graphql/EmployeeServiceApplicationTest.java @@ -15,103 +15,103 @@ import java.util.Collections; @SpringBootTest() class EmployeeServiceApplicationTest { - @Autowired - private ReactiveWebApplicationContext context; - private static final String BASE_URL = "https://spring.example.org/graphql"; + @Autowired + private ReactiveWebApplicationContext context; + private static final String BASE_URL = "https://spring.example.org/graphql"; - WebTestClient client; + WebTestClient client; - @BeforeEach - public void setup() { - this.client = WebTestClient - .bindToApplicationContext(this.context) - .apply(SecurityMockServerConfigurers.springSecurity()) - .configureClient() - .filter(ExchangeFilterFunctions.basicAuthentication()) - .defaultHeaders(headers -> { - headers.setContentType(MediaType.APPLICATION_JSON); - headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); - }) - .baseUrl(BASE_URL) - .build(); - } + @BeforeEach + public void setup() { + this.client = WebTestClient + .bindToApplicationContext(this.context) + .apply(SecurityMockServerConfigurers.springSecurity()) + .configureClient() + .filter(ExchangeFilterFunctions.basicAuthentication()) + .defaultHeaders(headers -> { + headers.setContentType(MediaType.APPLICATION_JSON); + headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); + }) + .baseUrl(BASE_URL) + .build(); + } - @Test - void canQueryName() { - String query = "{" + - " employees{ " + - " name" + - " }" + - "}"; + @Test + void canQueryName() { + String query = "{" + + " employees{ " + + " name" + + " }" + + "}"; - client.post().uri("") - .bodyValue("{ \"query\": \"" + query + "\"}") - .exchange() - .expectStatus().isOk() - .expectBody().jsonPath("data.employees[0].name").isEqualTo("Andi"); + client.post().uri("") + .bodyValue("{ \"query\": \"" + query + "\"}") + .exchange() + .expectStatus().isOk() + .expectBody().jsonPath("data.employees[0].name").isEqualTo("Andi"); - } + } - @Test - void canNotQuerySalary() { - String query = "{" + - " employees{ " + - " name" + - " salary" + - " }" + - "}"; + @Test + void canNotQuerySalary() { + String query = "{" + + " employees{ " + + " name" + + " salary" + + " }" + + "}"; - client.post().uri("") - .bodyValue("{ \"query\": \"" + query + "\"}") - .exchange() - .expectStatus().isOk() - .expectBody() - .jsonPath("data.employees[0].name").isEqualTo("Andi") - .jsonPath("data.employees[0].salary").doesNotExist(); + client.post().uri("") + .bodyValue("{ \"query\": \"" + query + "\"}") + .exchange() + .expectStatus().isOk() + .expectBody() + .jsonPath("data.employees[0].name").isEqualTo("Andi") + .jsonPath("data.employees[0].salary").doesNotExist(); - } + } - @Test - void canQuerySalaryAsAdmin() { - String query = "{" + - " employees{ " + - " name" + - " salary" + - " }" + - "}"; + @Test + void canQuerySalaryAsAdmin() { + String query = "{" + + " employees{ " + + " name" + + " salary" + + " }" + + "}"; - client.post().uri("") - .headers(h -> h.setBasicAuth("admin", "admin")) - .bodyValue("{ \"query\": \"" + query + "\"}") - .exchange() - .expectStatus().isOk() - .expectBody() - .jsonPath("data.employees[0].name").isEqualTo("Andi") - .jsonPath("data.employees[0].salary").isEqualTo("42"); + client.post().uri("") + .headers(h -> h.setBasicAuth("admin", "admin")) + .bodyValue("{ \"query\": \"" + query + "\"}") + .exchange() + .expectStatus().isOk() + .expectBody() + .jsonPath("data.employees[0].name").isEqualTo("Andi") + .jsonPath("data.employees[0].salary").isEqualTo("42"); - } + } - @Test - void invalidCredentials() { - String query = "{" + - " employees{ " + - " name" + - " salary" + - " }" + - "}"; + @Test + void invalidCredentials() { + String query = "{" + + " employees{ " + + " name" + + " salary" + + " }" + + "}"; - client.post().uri("") - .headers(h -> h.setBasicAuth("admin", "INVALID")) - .bodyValue("{ \"query\": \"" + query + "\"}") - .exchange() - .expectStatus().isUnauthorized() - .expectBody() - .isEmpty(); + client.post().uri("") + .headers(h -> h.setBasicAuth("admin", "INVALID")) + .bodyValue("{ \"query\": \"" + query + "\"}") + .exchange() + .expectStatus().isUnauthorized() + .expectBody() + .isEmpty(); - } + } } \ No newline at end of file From 9a61207f2de033bd8cc7b0df5b54a9946309bad0 Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Wed, 2 Jun 2021 14:15:57 -0500 Subject: [PATCH 10/12] Remove DataFetchers Follow the conventions of the other sample projects. --- .../spring/sample/graphql/DataFetchers.java | 37 -------------- .../spring/sample/graphql/GraphQLWiring.java | 34 ------------- .../spring/sample/graphql/SampleWiring.java | 49 +++++++++++++++++++ 3 files changed, 49 insertions(+), 71 deletions(-) delete mode 100644 samples/webflux-security/src/main/java/io/spring/sample/graphql/DataFetchers.java delete mode 100644 samples/webflux-security/src/main/java/io/spring/sample/graphql/GraphQLWiring.java create mode 100644 samples/webflux-security/src/main/java/io/spring/sample/graphql/SampleWiring.java diff --git a/samples/webflux-security/src/main/java/io/spring/sample/graphql/DataFetchers.java b/samples/webflux-security/src/main/java/io/spring/sample/graphql/DataFetchers.java deleted file mode 100644 index 530cc9da..00000000 --- a/samples/webflux-security/src/main/java/io/spring/sample/graphql/DataFetchers.java +++ /dev/null @@ -1,37 +0,0 @@ -package io.spring.sample.graphql; - -import java.math.BigDecimal; -import java.util.Map; - -import graphql.schema.DataFetcher; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -@Component -public class DataFetchers { - - @Autowired - EmployeeService employeeService; - - public DataFetcher employeeDataFetcher = env -> { - return employeeService.getAllEmployees(); - }; - - @Autowired - SalaryService salaryService; - - public DataFetcher salaryDataFetcher = env -> { - Employee employee = env.getSource(); - return salaryService.getSalaryForEmployee(employee); - }; - - public DataFetcher updateSalaryFetcher = env -> { - Map input = env.getArgument("input"); - String employeeId = input.get("employeeId"); - BigDecimal newSalary = new BigDecimal(input.get("salary")); - salaryService.updateSalary(employeeId, newSalary); - return null; - }; - -} diff --git a/samples/webflux-security/src/main/java/io/spring/sample/graphql/GraphQLWiring.java b/samples/webflux-security/src/main/java/io/spring/sample/graphql/GraphQLWiring.java deleted file mode 100644 index c162c86f..00000000 --- a/samples/webflux-security/src/main/java/io/spring/sample/graphql/GraphQLWiring.java +++ /dev/null @@ -1,34 +0,0 @@ -package io.spring.sample.graphql; - -import graphql.schema.idl.RuntimeWiring; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.graphql.boot.RuntimeWiringCustomizer; -import org.springframework.stereotype.Component; - -@Component -public class GraphQLWiring implements RuntimeWiringCustomizer { - - @Autowired - DataFetchers dataFetchers; - - @Override - public void customize(RuntimeWiring.Builder builder) { - builder.type("Query", - wiringBuilder -> { - return wiringBuilder. - dataFetcher("employees", dataFetchers.employeeDataFetcher); - }); - builder.type("Employee", - wiringBuilder -> { - return wiringBuilder. - dataFetcher("salary", dataFetchers.salaryDataFetcher); - }); - builder.type("Mutation", - wiringBuilder -> { - return wiringBuilder. - dataFetcher("updateSalary", dataFetchers.updateSalaryFetcher); - }); - - } -} diff --git a/samples/webflux-security/src/main/java/io/spring/sample/graphql/SampleWiring.java b/samples/webflux-security/src/main/java/io/spring/sample/graphql/SampleWiring.java new file mode 100644 index 00000000..69fdf12d --- /dev/null +++ b/samples/webflux-security/src/main/java/io/spring/sample/graphql/SampleWiring.java @@ -0,0 +1,49 @@ +package io.spring.sample.graphql; + +import java.math.BigDecimal; +import java.util.Map; + +import graphql.schema.idl.RuntimeWiring; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.graphql.boot.RuntimeWiringCustomizer; +import org.springframework.stereotype.Component; + +@Component +public class SampleWiring implements RuntimeWiringCustomizer { + + final EmployeeService employeeService; + + final SalaryService salaryService; + + public SampleWiring(EmployeeService employeeService, SalaryService salaryService) { + this.employeeService = employeeService; + this.salaryService = salaryService; + } + + + @Override + public void customize(RuntimeWiring.Builder builder) { + builder.type("Query", wiringBuilder -> + wiringBuilder.dataFetcher("employees", env -> + employeeService.getAllEmployees() + ) + ); + builder.type("Employee", wiringBuilder -> + wiringBuilder.dataFetcher("salary", env -> { + Employee employee = env.getSource(); + return salaryService.getSalaryForEmployee(employee); + }) + ); + builder.type("Mutation", wiringBuilder -> + wiringBuilder.dataFetcher("updateSalary", env -> { + Map input = env.getArgument("input"); + String employeeId = input.get("employeeId"); + BigDecimal newSalary = new BigDecimal(input.get("salary")); + salaryService.updateSalary(employeeId, newSalary); + return null; + }) + ); + } + +} From d139e97cdb2ee3071207c776f344cb22b686a6bb Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Wed, 2 Jun 2021 16:41:35 -0500 Subject: [PATCH 11/12] Rename to SampleApplication Follow convention of other sample applications. --- ...EmployeeServiceApplication.java => SampleApplication.java} | 4 ++-- ...erviceApplicationTest.java => SampleApplicationTests.java} | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename samples/webflux-security/src/main/java/io/spring/sample/graphql/{EmployeeServiceApplication.java => SampleApplication.java} (88%) rename samples/webflux-security/src/test/java/io/spring/sample/graphql/{EmployeeServiceApplicationTest.java => SampleApplicationTests.java} (98%) diff --git a/samples/webflux-security/src/main/java/io/spring/sample/graphql/EmployeeServiceApplication.java b/samples/webflux-security/src/main/java/io/spring/sample/graphql/SampleApplication.java similarity index 88% rename from samples/webflux-security/src/main/java/io/spring/sample/graphql/EmployeeServiceApplication.java rename to samples/webflux-security/src/main/java/io/spring/sample/graphql/SampleApplication.java index a8678a85..19225fa6 100644 --- a/samples/webflux-security/src/main/java/io/spring/sample/graphql/EmployeeServiceApplication.java +++ b/samples/webflux-security/src/main/java/io/spring/sample/graphql/SampleApplication.java @@ -20,9 +20,9 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication -public class EmployeeServiceApplication { +public class SampleApplication { public static void main(String[] args) { - SpringApplication.run(EmployeeServiceApplication.class, args); + SpringApplication.run(SampleApplication.class, args); } } diff --git a/samples/webflux-security/src/test/java/io/spring/sample/graphql/EmployeeServiceApplicationTest.java b/samples/webflux-security/src/test/java/io/spring/sample/graphql/SampleApplicationTests.java similarity index 98% rename from samples/webflux-security/src/test/java/io/spring/sample/graphql/EmployeeServiceApplicationTest.java rename to samples/webflux-security/src/test/java/io/spring/sample/graphql/SampleApplicationTests.java index bb1a4bc9..24ef3c33 100644 --- a/samples/webflux-security/src/test/java/io/spring/sample/graphql/EmployeeServiceApplicationTest.java +++ b/samples/webflux-security/src/test/java/io/spring/sample/graphql/SampleApplicationTests.java @@ -13,7 +13,7 @@ import org.springframework.web.reactive.function.client.ExchangeFilterFunctions; import java.util.Collections; @SpringBootTest() -class EmployeeServiceApplicationTest { +class SampleApplicationTests { @Autowired private ReactiveWebApplicationContext context; From 17a56a1000ea758409ee48951fea9e24ec4c15f5 Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Wed, 2 Jun 2021 17:05:52 -0500 Subject: [PATCH 12/12] Use New Lamda Security Syntax --- .../io/spring/sample/graphql/SecurityConfig.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/samples/webflux-security/src/main/java/io/spring/sample/graphql/SecurityConfig.java b/samples/webflux-security/src/main/java/io/spring/sample/graphql/SecurityConfig.java index d0a139f5..145b6ea1 100644 --- a/samples/webflux-security/src/main/java/io/spring/sample/graphql/SecurityConfig.java +++ b/samples/webflux-security/src/main/java/io/spring/sample/graphql/SecurityConfig.java @@ -10,6 +10,8 @@ import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.web.server.SecurityWebFilterChain; +import static org.springframework.security.config.Customizer.withDefaults; + @Configuration @EnableWebFluxSecurity @EnableReactiveMethodSecurity @@ -17,14 +19,14 @@ public class SecurityConfig { @Bean SecurityWebFilterChain springWebFilterChain(ServerHttpSecurity http) throws Exception { - http.csrf().disable(); return http + .csrf(c -> c.disable()) // Demonstrate that method security works // Best practice to use both for defense in depth - .authorizeExchange() - .anyExchange().permitAll() - .and() - .httpBasic().and() + .authorizeExchange(requests -> requests + .anyExchange().permitAll() + ) + .httpBasic(withDefaults()) .build(); }