Skip to content
This repository was archived by the owner on Dec 19, 2023. It is now read-only.

Commit 5500517

Browse files
committed
Add CSRF header if provided fix #253
1 parent 281b8cf commit 5500517

File tree

4 files changed

+66
-44
lines changed

4 files changed

+66
-44
lines changed

graphiql-spring-boot-autoconfigure/build.gradle

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,11 @@
1818
*/
1919
dependencies{
2020
annotationProcessor "org.springframework.boot:spring-boot-configuration-processor:$LIB_SPRING_BOOT_VER"
21-
21+
2222
compile "org.springframework.boot:spring-boot-autoconfigure:$LIB_SPRING_BOOT_VER"
2323
compile "org.apache.commons:commons-text:1.1"
2424
compileOnly "org.springframework.boot:spring-boot-starter-web:$LIB_SPRING_BOOT_VER"
25+
compileOnly "org.springframework.boot:spring-boot-starter-security:$LIB_SPRING_BOOT_VER"
2526

2627
testCompile "org.springframework.boot:spring-boot-starter-web:$LIB_SPRING_BOOT_VER"
2728
testCompile "org.springframework.boot:spring-boot-starter-test:$LIB_SPRING_BOOT_VER"

graphiql-spring-boot-autoconfigure/src/main/java/com/oembedler/moon/graphiql/boot/GraphiQLController.java

Lines changed: 32 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,18 @@
22

33
import com.fasterxml.jackson.core.JsonProcessingException;
44
import com.fasterxml.jackson.databind.ObjectMapper;
5+
import lombok.extern.slf4j.Slf4j;
56
import org.apache.commons.lang3.StringUtils;
67
import org.apache.commons.lang3.text.StrSubstitutor;
78
import org.springframework.beans.factory.annotation.Autowired;
8-
import org.springframework.beans.factory.annotation.Value;
99
import org.springframework.core.env.Environment;
1010
import org.springframework.core.io.ClassPathResource;
1111
import org.springframework.http.MediaType;
12+
import org.springframework.security.web.csrf.CsrfToken;
1213
import org.springframework.stereotype.Controller;
1314
import org.springframework.util.StreamUtils;
15+
import org.springframework.web.bind.annotation.GetMapping;
1416
import org.springframework.web.bind.annotation.PathVariable;
15-
import org.springframework.web.bind.annotation.RequestMapping;
1617
import org.springframework.web.bind.annotation.RequestParam;
1718

1819
import javax.annotation.PostConstruct;
@@ -28,6 +29,7 @@
2829
/**
2930
* @author Andrew Potter
3031
*/
32+
@Slf4j
3133
@Controller
3234
public class GraphiQLController {
3335

@@ -36,30 +38,6 @@ public class GraphiQLController {
3638
private static final String GRAPHIQL = "graphiql";
3739
private static final String FAVICON_GRAPHQL_ORG = "//graphql.org/img/favicon.png";
3840

39-
@Value("${graphiql.endpoint.graphql:/graphql}")
40-
private String graphqlEndpoint;
41-
42-
@Value("${graphiql.endpoint.subscriptions:/subscriptions}")
43-
private String subscriptionsEndpoint;
44-
45-
@Value("${graphiql.static.basePath:/}")
46-
private String staticBasePath;
47-
48-
@Value("${graphiql.pageTitle:GraphiQL}")
49-
private String pageTitle;
50-
51-
@Value("${graphiql.cdn.enabled:false}")
52-
private Boolean graphiqlCdnEnabled;
53-
54-
@Value("${graphiql.cdn.version:0.13.0}")
55-
private String graphiqlCdnVersion;
56-
57-
@Value("${graphiql.subscriptions.timeout:30}")
58-
private Integer subscriptionsTimeout;
59-
60-
@Value("${graphiql.subscriptions.reconnect:false}")
61-
private Boolean subscriptionsReconnect;
62-
6341
@Autowired
6442
private Environment environment;
6543

@@ -68,7 +46,7 @@ public class GraphiQLController {
6846

6947
private String template;
7048
private String props;
71-
private String headers;
49+
private Properties headerProperties;
7250

7351
@PostConstruct
7452
public void onceConstructed() throws IOException {
@@ -87,12 +65,11 @@ private void loadProps() throws IOException {
8765
props = new PropsLoader(environment).load();
8866
}
8967

90-
private void loadHeaders() throws JsonProcessingException {
68+
private void loadHeaders() {
9169
PropertyGroupReader propertyReader = new PropertyGroupReader(environment, "graphiql.headers.");
92-
Properties headerProperties = propertyReader.load();
70+
headerProperties = propertyReader.load();
9371
addIfAbsent(headerProperties, "Accept");
9472
addIfAbsent(headerProperties, "Content-Type");
95-
this.headers = new ObjectMapper().writeValueAsString(headerProperties);
9673
}
9774

9875
private void addIfAbsent(Properties headerProperties, String header) {
@@ -101,26 +78,35 @@ private void addIfAbsent(Properties headerProperties, String header) {
10178
}
10279
}
10380

104-
@RequestMapping(value = "${graphiql.mapping:/graphiql}")
81+
@GetMapping(value = "${graphiql.mapping:/graphiql}")
10582
public void graphiql(HttpServletRequest request, HttpServletResponse response, @PathVariable Map<String, String> params) throws IOException {
10683
response.setContentType("text/html; charset=UTF-8");
84+
Object csrf = request.getAttribute("_csrf");
85+
if (csrf != null) {
86+
CsrfToken csrfToken = (CsrfToken) csrf;
87+
headerProperties.setProperty(csrfToken.getHeaderName(), csrfToken.getToken());
88+
}
10789

10890
Map<String, String> replacements = getReplacements(
10991
constructGraphQlEndpoint(request, params),
110-
request.getContextPath() + subscriptionsEndpoint,
111-
request.getContextPath() + staticBasePath
92+
request.getContextPath() + graphiQLProperties.getEndpoint().getSubscriptions(),
93+
request.getContextPath() + graphiQLProperties.getSTATIC().getBasePath()
11294
);
11395

11496
String populatedTemplate = StrSubstitutor.replace(template, replacements);
11597
response.getOutputStream().write(populatedTemplate.getBytes(Charset.defaultCharset()));
11698
}
11799

118-
private Map<String, String> getReplacements(String graphqlEndpoint, String subscriptionsEndpoint, String staticBasePath) {
100+
private Map<String, String> getReplacements(
101+
String graphqlEndpoint,
102+
String subscriptionsEndpoint,
103+
String staticBasePath
104+
) {
119105
Map<String, String> replacements = new HashMap<>();
120106
replacements.put("graphqlEndpoint", graphqlEndpoint);
121107
replacements.put("subscriptionsEndpoint", subscriptionsEndpoint);
122108
replacements.put("staticBasePath", staticBasePath);
123-
replacements.put("pageTitle", pageTitle);
109+
replacements.put("pageTitle", graphiQLProperties.getPageTitle());
124110
replacements.put("pageFavicon", getResourceUrl(staticBasePath, "favicon.ico", FAVICON_GRAPHQL_ORG));
125111
replacements.put("es6PromiseJsUrl", getResourceUrl(staticBasePath, "es6-promise.auto.min.js",
126112
joinCdnjsPath("es6-promise", "4.1.1", "es6-promise.auto.min.js")));
@@ -131,19 +117,23 @@ private Map<String, String> getReplacements(String graphqlEndpoint, String subsc
131117
replacements.put("reactDomJsUrl", getResourceUrl(staticBasePath, "react-dom.min.js",
132118
joinCdnjsPath("react-dom", "16.8.3", "umd/react-dom.production.min.js")));
133119
replacements.put("graphiqlCssUrl", getResourceUrl(staticBasePath, "graphiql.min.css",
134-
joinJsDelivrPath(GRAPHIQL, graphiqlCdnVersion, "graphiql.css")));
120+
joinJsDelivrPath(GRAPHIQL, graphiQLProperties.getCdn().getVersion(), "graphiql.css")));
135121
replacements.put("graphiqlJsUrl", getResourceUrl(staticBasePath, "graphiql.min.js",
136-
joinJsDelivrPath(GRAPHIQL, graphiqlCdnVersion, "graphiql.min.js")));
122+
joinJsDelivrPath(GRAPHIQL, graphiQLProperties.getCdn().getVersion(), "graphiql.min.js")));
137123
replacements.put("subscriptionsTransportWsBrowserClientUrl", getResourceUrl(staticBasePath,
138124
"subscriptions-transport-ws-browser-client.js",
139125
joinJsDelivrPath("subscriptions-transport-ws", "0.9.15", "browser/client.js")));
140126
replacements.put("graphiqlSubscriptionsFetcherBrowserClientUrl", getResourceUrl(staticBasePath,
141127
"graphiql-subscriptions-fetcher-browser-client.js",
142128
joinJsDelivrPath("graphiql-subscriptions-fetcher", "0.0.2", "browser/client.js")));
143129
replacements.put("props", props);
144-
replacements.put("headers", headers);
145-
replacements.put("subscriptionClientTimeout", String.valueOf(subscriptionsTimeout * 1000));
146-
replacements.put("subscriptionClientReconnect", String.valueOf(subscriptionsReconnect));
130+
try {
131+
replacements.put("headers", new ObjectMapper().writeValueAsString(headerProperties));
132+
} catch (JsonProcessingException e) {
133+
log.error("Cannot serialize headers", e);
134+
}
135+
replacements.put("subscriptionClientTimeout", String.valueOf(graphiQLProperties.getSubscriptions().getTimeout() * 1000));
136+
replacements.put("subscriptionClientReconnect", String.valueOf(graphiQLProperties.getSubscriptions().isReconnect()));
147137
replacements.put("editorThemeCss", getEditorThemeCssURL());
148138
return replacements;
149139
}
@@ -161,7 +151,7 @@ private String getEditorThemeCssURL() {
161151
}
162152

163153
private String getResourceUrl(String staticBasePath, String staticFileName, String cdnUrl) {
164-
if (graphiqlCdnEnabled && StringUtils.isNotBlank(cdnUrl)) {
154+
if (graphiQLProperties.getCdn().isEnabled() && StringUtils.isNotBlank(cdnUrl)) {
165155
return cdnUrl;
166156
}
167157
return joinStaticPath(staticBasePath, staticFileName);
@@ -180,7 +170,7 @@ private String joinJsDelivrPath(String library, String cdnVersion, String cdnFil
180170
}
181171

182172
private String constructGraphQlEndpoint(HttpServletRequest request, @RequestParam Map<String, String> params) {
183-
String endpoint = graphqlEndpoint;
173+
String endpoint = graphiQLProperties.getEndpoint().getGraphql();
184174
for (Map.Entry<String, String> param : params.entrySet()) {
185175
endpoint = endpoint.replaceAll("\\{" + param.getKey() + "}", param.getValue());
186176
}

graphiql-spring-boot-autoconfigure/src/main/java/com/oembedler/moon/graphiql/boot/GraphiQLProperties.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,32 @@
22

33
import lombok.Data;
44
import org.springframework.boot.context.properties.ConfigurationProperties;
5+
import org.springframework.boot.context.properties.ConfigurationPropertiesBinding;
6+
import org.springframework.boot.context.properties.NestedConfigurationProperty;
57

68
@Data
79
@ConfigurationProperties("graphiql")
810
class GraphiQLProperties {
911

12+
private Endpoint endpoint = new Endpoint();
13+
private Static STATIC = new Static();
1014
private CodeMirror codeMirror = new CodeMirror();
1115
private Props props = new Props();
16+
private String pageTitle = "GraphiQL";
17+
private String mapping = "/graphiql";
18+
private Subscriptions subscriptions = new Subscriptions();
19+
private Cdn cdn = new Cdn();
20+
21+
@Data
22+
static class Endpoint {
23+
private String graphql = "/graphql";
24+
private String subscriptions = "/subscriptions";
25+
}
26+
27+
@Data
28+
static class Static {
29+
private String basePath = "/";
30+
}
1231

1332
@Data
1433
static class CodeMirror {
@@ -25,4 +44,16 @@ static class Variables {
2544
private String editorTheme;
2645
}
2746
}
47+
48+
@Data
49+
static class Cdn {
50+
private boolean enabled = false;
51+
private String version = "0.13.0";
52+
}
53+
54+
@Data
55+
static class Subscriptions {
56+
private int timeout = 30;
57+
private boolean reconnect = false;
58+
}
2859
}

graphiql-spring-boot-autoconfigure/src/main/resources/graphiql.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@
157157
// Render <GraphiQL /> into the body.
158158
ReactDOM.render(
159159
React.createElement(GraphiQL, props),
160-
document.getElementById("splash")
160+
document.body
161161
);
162162

163163
</script>

0 commit comments

Comments
 (0)