pull/30774/head
Phillip Webb 3 years ago
parent 71acc90da8
commit 36f1249fc6

@ -295,6 +295,7 @@ class PrometheusMetricsExportAutoConfigurationTests {
@Bean @Bean
SpanContextSupplier spanContextSupplier() { SpanContextSupplier spanContextSupplier() {
return new SpanContextSupplier() { return new SpanContextSupplier() {
@Override @Override
public String getTraceId() { public String getTraceId() {
return null; return null;
@ -304,6 +305,7 @@ class PrometheusMetricsExportAutoConfigurationTests {
public String getSpanId() { public String getSpanId() {
return null; return null;
} }
}; };
} }

@ -58,7 +58,6 @@ class DefaultGraphQlSchemaCondition extends SpringBootCondition implements Confi
boolean match = false; boolean match = false;
List<ConditionMessage> messages = new ArrayList<>(2); List<ConditionMessage> messages = new ArrayList<>(2);
ConditionMessage.Builder message = ConditionMessage.forCondition(ConditionalOnGraphQlSchema.class); ConditionMessage.Builder message = ConditionMessage.forCondition(ConditionalOnGraphQlSchema.class);
Binder binder = Binder.get(context.getEnvironment()); Binder binder = Binder.get(context.getEnvironment());
GraphQlProperties.Schema schema = binder.bind("spring.graphql.schema", GraphQlProperties.Schema.class) GraphQlProperties.Schema schema = binder.bind("spring.graphql.schema", GraphQlProperties.Schema.class)
.orElse(new GraphQlProperties.Schema()); .orElse(new GraphQlProperties.Schema());

@ -33,9 +33,10 @@ import org.springframework.util.MimeTypeUtils;
/** /**
* {@link EnableAutoConfiguration Auto-configuration} for {@link RSocketGraphQlClient}. * {@link EnableAutoConfiguration Auto-configuration} for {@link RSocketGraphQlClient}.
* This auto-configuration creates {@link RSocketGraphQlClient.Builder} prototype beans, * This auto-configuration creates
* as the builders are stateful and should not be reused to build client instances with * {@link org.springframework.graphql.client.RSocketGraphQlClient.Builder
* different configurations. * RSocketGraphQlClient.Builder} prototype beans, as the builders are stateful and should
* not be reused to build client instances with different configurations.
* *
* @author Brian Clozel * @author Brian Clozel
* @since 2.7.0 * @since 2.7.0

@ -30,6 +30,7 @@ import org.springframework.security.saml2.provider.service.registration.Saml2Mes
* *
* @author Madhura Bhave * @author Madhura Bhave
* @author Phillip Webb * @author Phillip Webb
* @author Moritz Halbritter
* @since 2.2.0 * @since 2.2.0
*/ */
@ConfigurationProperties("spring.security.saml2.relyingparty") @ConfigurationProperties("spring.security.saml2.relyingparty")

@ -40,6 +40,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.core.log.LogMessage;
import org.springframework.security.converter.RsaKeyConverters; import org.springframework.security.converter.RsaKeyConverters;
import org.springframework.security.saml2.core.Saml2X509Credential; import org.springframework.security.saml2.core.Saml2X509Credential;
import org.springframework.security.saml2.core.Saml2X509Credential.Saml2X509CredentialType; import org.springframework.security.saml2.core.Saml2X509Credential.Saml2X509CredentialType;
@ -49,6 +50,7 @@ import org.springframework.security.saml2.provider.service.registration.RelyingP
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration.Builder; import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration.Builder;
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository; import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrations; import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrations;
import org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
@ -58,6 +60,7 @@ import org.springframework.util.StringUtils;
* *
* @author Madhura Bhave * @author Madhura Bhave
* @author Phillip Webb * @author Phillip Webb
* @author Moritz Halbritter
*/ */
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
@Conditional(RegistrationConfiguredCondition.class) @Conditional(RegistrationConfiguredCondition.class)
@ -78,12 +81,11 @@ class Saml2RelyingPartyRegistrationConfiguration {
} }
private RelyingPartyRegistration asRegistration(String id, Registration properties) { private RelyingPartyRegistration asRegistration(String id, Registration properties) {
boolean usingMetadata = StringUtils AssertingPartyProperties assertingParty = new AssertingPartyProperties(properties, id);
.hasText(getFromAssertingParty(properties, id, "metadata-uri", AssertingParty::getMetadataUri)); boolean usingMetadata = StringUtils.hasText(assertingParty.getMetadataUri());
Builder builder = (usingMetadata) ? RelyingPartyRegistrations Builder builder = (usingMetadata)
.fromMetadataLocation( ? RelyingPartyRegistrations.fromMetadataLocation(assertingParty.getMetadataUri()).registrationId(id)
getFromAssertingParty(properties, id, "metadata-uri", AssertingParty::getMetadataUri)) : RelyingPartyRegistration.withRegistrationId(id);
.registrationId(id) : RelyingPartyRegistration.withRegistrationId(id);
builder.assertionConsumerServiceLocation(properties.getAcs().getLocation()); builder.assertionConsumerServiceLocation(properties.getAcs().getLocation());
builder.assertionConsumerServiceBinding(properties.getAcs().getBinding()); builder.assertionConsumerServiceBinding(properties.getAcs().getBinding());
builder.assertingPartyDetails(mapAssertingParty(properties, id, usingMetadata)); builder.assertingPartyDetails(mapAssertingParty(properties, id, usingMetadata));
@ -91,8 +93,8 @@ class Saml2RelyingPartyRegistrationConfiguration {
.map(this::asSigningCredential).forEach(credentials::add)); .map(this::asSigningCredential).forEach(credentials::add));
builder.decryptionX509Credentials((credentials) -> properties.getDecryption().getCredentials().stream() builder.decryptionX509Credentials((credentials) -> properties.getDecryption().getCredentials().stream()
.map(this::asDecryptionCredential).forEach(credentials::add)); .map(this::asDecryptionCredential).forEach(credentials::add));
builder.assertingPartyDetails((details) -> details.verificationX509Credentials( builder.assertingPartyDetails(
(credentials) -> getFromAssertingParty(properties, id, "verification", AssertingParty::getVerification) (details) -> details.verificationX509Credentials((credentials) -> assertingParty.getVerification()
.getCredentials().stream().map(this::asVerificationCredential).forEach(credentials::add))); .getCredentials().stream().map(this::asVerificationCredential).forEach(credentials::add)));
builder.entityId(properties.getEntityId()); builder.entityId(properties.getEntityId());
RelyingPartyRegistration registration = builder.build(); RelyingPartyRegistration registration = builder.build();
@ -101,35 +103,15 @@ class Saml2RelyingPartyRegistrationConfiguration {
return registration; return registration;
} }
@SuppressWarnings("deprecation")
private <T> T getFromAssertingParty(Registration registration, String id, String name,
Function<AssertingParty, T> getter) {
T newValue = getter.apply(registration.getAssertingParty());
if (newValue != null) {
return newValue;
}
T deprecatedValue = getter.apply(registration.getIdentityprovider());
if (deprecatedValue != null) {
logger.warn(String.format(
"Property 'spring.security.saml2.relyingparty.registration.identityprovider.%1$s.%2$s' is deprecated, please use 'spring.security.saml2.relyingparty.registration.asserting-party.%1$s.%2$s' instead",
id, name));
return deprecatedValue;
}
return newValue;
}
private Consumer<AssertingPartyDetails.Builder> mapAssertingParty(Registration registration, String id, private Consumer<AssertingPartyDetails.Builder> mapAssertingParty(Registration registration, String id,
boolean usingMetadata) { boolean usingMetadata) {
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
return (details) -> { return (details) -> {
map.from(() -> getFromAssertingParty(registration, id, "entity-id", AssertingParty::getEntityId)) AssertingPartyProperties assertingParty = new AssertingPartyProperties(registration, id);
.to(details::entityId); PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
map.from(() -> getFromAssertingParty(registration, id, "singlesignon.binding", map.from(assertingParty::getEntityId).to(details::entityId);
(property) -> property.getSinglesignon().getBinding())).to(details::singleSignOnServiceBinding); map.from(assertingParty::getSingleSignonBinding).to(details::singleSignOnServiceBinding);
map.from(() -> getFromAssertingParty(registration, id, "singlesignon.url", map.from(assertingParty::getSingleSignonUrl).to(details::singleSignOnServiceLocation);
(property) -> property.getSinglesignon().getUrl())).to(details::singleSignOnServiceLocation); map.from(assertingParty::getSingleSignonSignRequest).when((ignored) -> !usingMetadata)
map.from(() -> getFromAssertingParty(registration, id, "singlesignon.sign-request",
(property) -> property.getSinglesignon().getSignRequest())).when((ignored) -> !usingMetadata)
.to(details::wantAuthnRequestsSigned); .to(details::wantAuthnRequestsSigned);
}; };
} }
@ -181,4 +163,61 @@ class Saml2RelyingPartyRegistrationConfiguration {
} }
} }
/**
* Access to {@link AssertingParty} properties taking into account deprecations.
*/
private static class AssertingPartyProperties {
private final Registration registration;
private final String id;
AssertingPartyProperties(Registration registration, String id) {
this.registration = registration;
this.id = id;
}
String getMetadataUri() {
return get("metadata-uri", AssertingParty::getMetadataUri);
}
Verification getVerification() {
return get("verification", AssertingParty::getVerification);
}
String getEntityId() {
return get("entity-id", AssertingParty::getEntityId);
}
Saml2MessageBinding getSingleSignonBinding() {
return get("singlesignon.binding", (property) -> property.getSinglesignon().getBinding());
}
String getSingleSignonUrl() {
return get("singlesignon.url", (property) -> property.getSinglesignon().getUrl());
}
Boolean getSingleSignonSignRequest() {
return get("singlesignon.sign-request", (property) -> property.getSinglesignon().getSignRequest());
}
@SuppressWarnings("deprecation")
private <T> T get(String name, Function<AssertingParty, T> getter) {
T newValue = getter.apply(this.registration.getAssertingParty());
if (newValue != null) {
return newValue;
}
T deprecatedValue = getter.apply(this.registration.getIdentityprovider());
if (deprecatedValue != null) {
logger.warn(LogMessage.format(
"Property 'spring.security.saml2.relyingparty.registration.identityprovider.%1$s.%2$s' is deprecated, "
+ "please use 'spring.security.saml2.relyingparty.registration.asserting-party.%1$s.%2$s' instead",
this.id, name));
return deprecatedValue;
}
return newValue;
}
}
} }

@ -16,8 +16,6 @@
package org.springframework.boot.autoconfigure.security.servlet; package org.springframework.boot.autoconfigure.security.servlet;
import java.util.EnumSet;
import javax.servlet.DispatcherType; import javax.servlet.DispatcherType;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
@ -65,7 +63,9 @@ class SpringBootWebSecurityConfiguration {
@Bean @Bean
@Order(SecurityProperties.BASIC_AUTH_ORDER) @Order(SecurityProperties.BASIC_AUTH_ORDER)
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception { SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated().and().formLogin().and().httpBasic(); http.authorizeRequests().anyRequest().authenticated();
http.formLogin();
http.httpBasic();
return http.build(); return http.build();
} }
@ -83,7 +83,7 @@ class SpringBootWebSecurityConfiguration {
FilterRegistrationBean<ErrorPageSecurityFilter> errorPageSecurityFilter(ApplicationContext context) { FilterRegistrationBean<ErrorPageSecurityFilter> errorPageSecurityFilter(ApplicationContext context) {
FilterRegistrationBean<ErrorPageSecurityFilter> registration = new FilterRegistrationBean<>( FilterRegistrationBean<ErrorPageSecurityFilter> registration = new FilterRegistrationBean<>(
new ErrorPageSecurityFilter(context)); new ErrorPageSecurityFilter(context));
registration.setDispatcherTypes(EnumSet.of(DispatcherType.ERROR)); registration.setDispatcherTypes(DispatcherType.ERROR);
return registration; return registration;
} }

@ -53,6 +53,7 @@ import static org.mockito.Mockito.mock;
* Tests for {@link Saml2RelyingPartyAutoConfiguration}. * Tests for {@link Saml2RelyingPartyAutoConfiguration}.
* *
* @author Madhura Bhave * @author Madhura Bhave
* @author Moritz Halbritter
*/ */
class Saml2RelyingPartyAutoConfigurationTests { class Saml2RelyingPartyAutoConfigurationTests {

@ -31,7 +31,6 @@ Because GraphQL is transport-agnostic, you'll also need to have one or more addi
[[web.graphql.schema]] [[web.graphql.schema]]
=== GraphQL Schema === GraphQL Schema
A Spring GraphQL application requires a defined schema at startup. A Spring GraphQL application requires a defined schema at startup.
By default, you can write ".graphqls" or ".gqls" schema files under `src/main/resources/graphql/**` and Spring Boot will pick them up automatically. By default, you can write ".graphqls" or ".gqls" schema files under `src/main/resources/graphql/**` and Spring Boot will pick them up automatically.
You can customize the locations with configprop:spring.graphql.schema.locations[] and the file extensions with configprop:spring.graphql.schema.file-extensions[]. You can customize the locations with configprop:spring.graphql.schema.locations[] and the file extensions with configprop:spring.graphql.schema.file-extensions[].
@ -46,9 +45,10 @@ include::{docs-resources}/graphql/schema.graphqls[]
NOTE: By default, https://spec.graphql.org/draft/#sec-Introspection[field introspection] will be allowed on the schema as it is required for tools such as GraphiQL. NOTE: By default, https://spec.graphql.org/draft/#sec-Introspection[field introspection] will be allowed on the schema as it is required for tools such as GraphiQL.
If you wish to not expose information about the schema, you can disable introspection by setting configprop:spring.graphql.schema.introspection.enabled[] to `false`. If you wish to not expose information about the schema, you can disable introspection by setting configprop:spring.graphql.schema.introspection.enabled[] to `false`.
[[web.graphql.runtimewiring]] [[web.graphql.runtimewiring]]
=== GraphQL RuntimeWiring === GraphQL RuntimeWiring
The GraphQL Java `RuntimeWiring.Builder` can be used to register custom scalar types, directives, type resolvers, `DataFetcher`s, and more. The GraphQL Java `RuntimeWiring.Builder` can be used to register custom scalar types, directives, type resolvers, `DataFetcher`s, and more.
You can declare `RuntimeWiringConfigurer` beans in your Spring config to get access to the `RuntimeWiring.Builder`. You can declare `RuntimeWiringConfigurer` beans in your Spring config to get access to the `RuntimeWiring.Builder`.
Spring Boot detects such beans and adds them to the {spring-graphql-docs}#execution-graphqlsource[GraphQlSource builder]. Spring Boot detects such beans and adds them to the {spring-graphql-docs}#execution-graphqlsource[GraphQlSource builder].
@ -63,7 +63,6 @@ include::code:GreetingController[]
[[web.graphql.data-query]] [[web.graphql.data-query]]
=== Querydsl and QueryByExample Repositories support === Querydsl and QueryByExample Repositories support
Spring Data offers support for both Querydsl and QueryByExample repositories. Spring Data offers support for both Querydsl and QueryByExample repositories.
Spring GraphQL can {spring-graphql-docs}#data[configure Querydsl and QueryByExample repositories as `DataFetcher`]. Spring GraphQL can {spring-graphql-docs}#data[configure Querydsl and QueryByExample repositories as `DataFetcher`].
@ -80,9 +79,10 @@ are detected by Spring Boot and considered as candidates for `DataFetcher` for m
[[web.graphql.transports]] [[web.graphql.transports]]
=== Transports === Transports
[[web.graphql.transports.http-websocket]] [[web.graphql.transports.http-websocket]]
==== HTTP and WebSocket ==== HTTP and WebSocket
The GraphQL HTTP endpoint is at HTTP POST "/graphql" by default. The path can be customized with configprop:spring.graphql.path[]. The GraphQL HTTP endpoint is at HTTP POST "/graphql" by default. The path can be customized with configprop:spring.graphql.path[].
The GraphQL WebSocket endpoint is off by default. To enable it: The GraphQL WebSocket endpoint is off by default. To enable it:
@ -112,9 +112,9 @@ Spring Boot supports many configuration properties under the `spring.graphql.cor
---- ----
[[web.graphql.transports.rsocket]] [[web.graphql.transports.rsocket]]
==== RSocket ==== RSocket
RSocket is also supported as a transport, on top of WebSocket or TCP. RSocket is also supported as a transport, on top of WebSocket or TCP.
Once the <<messaging#messaging.rsocket.server-auto-configuration,RSocket server is configured>>, we can configure our GraphQL handler on a particular route using configprop:spring.graphql.rsocket.mapping[]. Once the <<messaging#messaging.rsocket.server-auto-configuration,RSocket server is configured>>, we can configure our GraphQL handler on a particular route using configprop:spring.graphql.rsocket.mapping[].
For example, configuring that mapping as `"graphql"` means we can use that as a route when sending requests with the `RSocketGraphQlClient`. For example, configuring that mapping as `"graphql"` means we can use that as a route when sending requests with the `RSocketGraphQlClient`.
@ -127,9 +127,9 @@ And then send a request:
include::code:RSocketGraphQlClientExample[tag=request] include::code:RSocketGraphQlClientExample[tag=request]
[[web.graphql.exception-handling]] [[web.graphql.exception-handling]]
=== Exceptions Handling === Exceptions Handling
Spring GraphQL enables applications to register one or more Spring `DataFetcherExceptionResolver` components that are invoked sequentially. Spring GraphQL enables applications to register one or more Spring `DataFetcherExceptionResolver` components that are invoked sequentially.
The Exception must be resolved to a list of `graphql.GraphQLError` objects, see {spring-graphql-docs}#execution-exceptions[Spring GraphQL exception handling documentation]. The Exception must be resolved to a list of `graphql.GraphQLError` objects, see {spring-graphql-docs}#execution-exceptions[Spring GraphQL exception handling documentation].
Spring Boot will automatically detect `DataFetcherExceptionResolver` beans and register them with the `GraphQlSource.Builder`. Spring Boot will automatically detect `DataFetcherExceptionResolver` beans and register them with the `GraphQlSource.Builder`.
@ -138,7 +138,6 @@ Spring Boot will automatically detect `DataFetcherExceptionResolver` beans and r
[[web.graphql.graphiql]] [[web.graphql.graphiql]]
=== GraphiQL and Schema printer === GraphiQL and Schema printer
Spring GraphQL offers infrastructure for helping developers when consuming or developing a GraphQL API. Spring GraphQL offers infrastructure for helping developers when consuming or developing a GraphQL API.
Spring GraphQL ships with a default https://github.com/graphql/graphiql[GraphiQL] page that is exposed at `"/graphiql"` by default. Spring GraphQL ships with a default https://github.com/graphql/graphiql[GraphiQL] page that is exposed at `"/graphiql"` by default.

@ -51,8 +51,7 @@ public class GraphQlTesterAutoConfiguration {
ObjectProvider<ObjectMapper> objectMapperProvider) { ObjectProvider<ObjectMapper> objectMapperProvider) {
ExecutionGraphQlServiceTester.Builder<?> builder = ExecutionGraphQlServiceTester.builder(graphQlService); ExecutionGraphQlServiceTester.Builder<?> builder = ExecutionGraphQlServiceTester.builder(graphQlService);
objectMapperProvider.ifAvailable((objectMapper) -> { objectMapperProvider.ifAvailable((objectMapper) -> {
final MediaType[] mediaTypes = new MediaType[] { MediaType.APPLICATION_JSON, MediaType[] mediaTypes = new MediaType[] { MediaType.APPLICATION_JSON, MediaType.APPLICATION_GRAPHQL };
MediaType.APPLICATION_GRAPHQL };
builder.encoder(new Jackson2JsonEncoder(objectMapper, mediaTypes)); builder.encoder(new Jackson2JsonEncoder(objectMapper, mediaTypes));
builder.decoder(new Jackson2JsonDecoder(objectMapper, mediaTypes)); builder.decoder(new Jackson2JsonDecoder(objectMapper, mediaTypes));
}); });

Loading…
Cancel
Save