Skip to content

Commit 30f0fe8

Browse files
committed
#528 Implement external access control based on previous PR
1 parent 78cdb00 commit 30f0fe8

File tree

18 files changed

+235
-23
lines changed

18 files changed

+235
-23
lines changed

openhtmltopdf-core/src/main/java/com/openhtmltopdf/extend/UserAgentCallback.java

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
*/
2020
package com.openhtmltopdf.extend;
2121

22+
import com.openhtmltopdf.outputdevice.helper.ExternalResourceType;
2223
import com.openhtmltopdf.resource.CSSResource;
2324
import com.openhtmltopdf.resource.ImageResource;
2425
import com.openhtmltopdf.resource.XMLResource;
@@ -51,29 +52,57 @@ public interface UserAgentCallback {
5152
* @param uri Location of the CSS
5253
* @return A CSSResource for the content at the URI.
5354
*/
54-
CSSResource getCSSResource(String uri);
55+
default CSSResource getCSSResource(String uri) {
56+
return getCSSResource(uri, ExternalResourceType.CSS);
57+
}
58+
59+
CSSResource getCSSResource(String uri, ExternalResourceType type);
5560

5661
/**
5762
* Retrieves the Image at the given URI. This is a synchronous call.
5863
*
5964
* @param uri Location of the image
6065
* @return An ImageResource for the content at the URI.
6166
*/
62-
ImageResource getImageResource(String uri);
67+
default ImageResource getImageResource(String uri) {
68+
return getImageResource(uri, ExternalResourceType.IMAGE_RASTER);
69+
}
70+
71+
ImageResource getImageResource(String uri, ExternalResourceType type);
72+
73+
74+
/**
75+
* @deprecated
76+
* Use {@link #getXMLResource(String, ExternalResourceType)} instead.
77+
*/
78+
@Deprecated
79+
default XMLResource getXMLResource(String uri) {
80+
return getXMLResource(uri, ExternalResourceType.XML_XHTML);
81+
}
6382

6483
/**
6584
* Retrieves the XML at the given URI. This is a synchronous call.
6685
*
6786
* @param uri Location of the XML
87+
* @param type Either xhtml or svg.
6888
* @return A XMLResource for the content at the URI.
6989
*/
70-
XMLResource getXMLResource(String uri);
71-
90+
XMLResource getXMLResource(String uri, ExternalResourceType type);
91+
92+
/**
93+
* @deprecated
94+
* Use {@link #getBinaryResource(String, ExternalResourceType)} instead.
95+
*/
96+
@Deprecated
97+
default byte[] getBinaryResource(String uri) {
98+
return getBinaryResource(uri, ExternalResourceType.BINARY);
99+
}
100+
72101
/**
73102
* Retrieves a binary resource located at a given URI and returns its contents
74103
* as a byte array or <code>null</code> if the resource could not be loaded.
75104
*/
76-
byte[] getBinaryResource(String uri);
105+
byte[] getBinaryResource(String uri, ExternalResourceType type);
77106

78107
/**
79108
* Normally, returns true if the user agent has visited this URI. UserAgent should consider

openhtmltopdf-core/src/main/java/com/openhtmltopdf/outputdevice/helper/BaseRendererBuilder.java

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.util.List;
2020
import java.util.Map;
2121
import java.util.Set;
22+
import java.util.function.BiPredicate;
2223
import java.util.function.Consumer;
2324

2425
/**
@@ -37,6 +38,10 @@ public abstract class BaseRendererBuilder<TFinalClass extends BaseRendererBuilde
3738
public abstract static class BaseRendererBuilderState {
3839
public final List<AddedFont> _fonts = new ArrayList<>();
3940
public final List<FSDOMMutator> _domMutators = new ArrayList<>();
41+
42+
public BiPredicate<String, ExternalResourceType> _beforeAccessController = new NaiveUserAgent.DefaultAccessController();
43+
public BiPredicate<String, ExternalResourceType> _afterAccessController = new NaiveUserAgent.DefaultAccessController();
44+
4045
public final Map<String, FSStreamFactory> _streamFactoryMap = new HashMap<>();
4146
public FSUriResolver _resolver;
4247
public String _html;
@@ -560,6 +565,38 @@ protected Closeable applyDiagnosticConsumer() {
560565
return ThreadCtx.applyDiagnosticConsumer(state._diagnosticConsumer);
561566
}
562567

568+
/**
569+
* Allows to set <strong>one</strong> external access controller to run
570+
* before the uri resolver and one to run after the uri resolver.
571+
*
572+
* The predicate will receive the uri and the resource type. If it returns
573+
* false, the resource will not be loaded but the rendering process will
574+
* attempt to continue without the resource.
575+
*
576+
* A default controller is registered that allows everything except file
577+
* embed resources. To override the default controller, register a controller
578+
* with after priority.
579+
*/
580+
public TFinalClass useExternalResourceAccessControl(
581+
BiPredicate<String, ExternalResourceType> allowExternalResource,
582+
ExternalResourceControlPriority priority) {
583+
if (priority == null) {
584+
// Default is after as safest.
585+
priority = ExternalResourceControlPriority.RUN_AFTER_RESOLVING_URI;
586+
}
587+
588+
switch (priority) {
589+
case RUN_AFTER_RESOLVING_URI:
590+
state._afterAccessController = allowExternalResource;
591+
break;
592+
case RUN_BEFORE_RESOLVING_URI:
593+
state._beforeAccessController = allowExternalResource;
594+
break;
595+
}
596+
597+
return (TFinalClass) this;
598+
}
599+
563600
public enum TextDirection {
564601
RTL, LTR
565602
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package com.openhtmltopdf.outputdevice.helper;
2+
3+
public enum ExternalResourceControlPriority {
4+
RUN_BEFORE_RESOLVING_URI,
5+
RUN_AFTER_RESOLVING_URI;
6+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.openhtmltopdf.outputdevice.helper;
2+
3+
public enum ExternalResourceType {
4+
FONT,
5+
FILE_EMBED,
6+
CSS,
7+
IMAGE_RASTER,
8+
XML_XHTML,
9+
XML_SVG,
10+
BINARY,
11+
PDF,
12+
SVG_BINARY;
13+
}

openhtmltopdf-core/src/main/java/com/openhtmltopdf/outputdevice/helper/FontFaceFontSupplier.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public FontFaceFontSupplier(SharedContext ctx, String src) {
2020

2121
@Override
2222
public InputStream supply() {
23-
byte[] font1 = ctx.getUserAgentCallback().getBinaryResource(src);
23+
byte[] font1 = ctx.getUserAgentCallback().getBinaryResource(src, ExternalResourceType.FONT);
2424

2525
if (font1 == null) {
2626
XRLog.log(Level.WARNING, LogMessageId.LogMessageId1Param.EXCEPTION_COULD_NOT_LOAD_FONT_FACE, src);

openhtmltopdf-core/src/main/java/com/openhtmltopdf/swing/NaiveUserAgent.java

Lines changed: 92 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,11 @@
2626
import java.net.URISyntaxException;
2727
import java.net.URL;
2828
import java.nio.charset.StandardCharsets;
29+
import java.util.EnumMap;
2930
import java.util.HashMap;
3031
import java.util.LinkedHashMap;
3132
import java.util.Map;
33+
import java.util.function.BiPredicate;
3234
import java.util.logging.Level;
3335

3436
import javax.imageio.ImageIO;
@@ -38,6 +40,8 @@
3840
import com.openhtmltopdf.extend.FSStreamFactory;
3941
import com.openhtmltopdf.extend.FSStream;
4042
import com.openhtmltopdf.extend.UserAgentCallback;
43+
import com.openhtmltopdf.outputdevice.helper.ExternalResourceControlPriority;
44+
import com.openhtmltopdf.outputdevice.helper.ExternalResourceType;
4145
import com.openhtmltopdf.resource.CSSResource;
4246
import com.openhtmltopdf.resource.ImageResource;
4347
import com.openhtmltopdf.resource.XMLResource;
@@ -63,6 +67,8 @@ public class NaiveUserAgent implements UserAgentCallback, DocumentListener {
6367
protected final LinkedHashMap<String, ImageResource> _imageCache = new LinkedHashMap<>();
6468
protected final FSUriResolver DEFAULT_URI_RESOLVER = new DefaultUriResolver();
6569

70+
protected final Map<ExternalResourceControlPriority, BiPredicate<String, ExternalResourceType>> _accessControllers = new EnumMap<>(ExternalResourceControlPriority.class);
71+
6672
protected FSUriResolver _resolver = DEFAULT_URI_RESOLVER;
6773
protected String _baseUri;
6874
protected Map<String, FSStreamFactory> _protocolsStreamFactory = new HashMap<>(2);
@@ -266,9 +272,15 @@ protected String readAll(Reader reader) throws IOException {
266272
* @return A CSSResource containing the CSS reader or null if not available.
267273
*/
268274
@Override
269-
public CSSResource getCSSResource(String uri) {
275+
public CSSResource getCSSResource(String uri, ExternalResourceType type) {
276+
if (!checkAccessAllowed(uri, type, ExternalResourceControlPriority.RUN_BEFORE_RESOLVING_URI)) {
277+
return null;
278+
}
270279
String resolved = _resolver.resolveURI(this._baseUri, uri);
271-
280+
if (!checkAccessAllowed(resolved, type, ExternalResourceControlPriority.RUN_AFTER_RESOLVING_URI)) {
281+
return null;
282+
}
283+
272284
if (resolved == null) {
273285
XRLog.log(Level.INFO, LogMessageId.LogMessageId2Param.LOAD_URI_RESOLVER_REJECTED_LOADING_AT_URI, "CSS resource", uri);
274286
return null;
@@ -286,11 +298,16 @@ public CSSResource getCSSResource(String uri) {
286298
* @return An ImageResource containing the image.
287299
*/
288300
@Override
289-
public ImageResource getImageResource(String uri) {
301+
public ImageResource getImageResource(String uri, ExternalResourceType type) {
290302
ImageResource ir;
291-
292303

304+
if (!checkAccessAllowed(uri, type, ExternalResourceControlPriority.RUN_BEFORE_RESOLVING_URI)) {
305+
return null;
306+
}
293307
String resolved = _resolver.resolveURI(this._baseUri, uri);
308+
if (!checkAccessAllowed(resolved, type, ExternalResourceControlPriority.RUN_AFTER_RESOLVING_URI)) {
309+
return null;
310+
}
294311

295312
if (resolved == null) {
296313
XRLog.log(Level.INFO, LogMessageId.LogMessageId2Param.LOAD_URI_RESOLVER_REJECTED_LOADING_AT_URI, "image resource", uri);
@@ -345,9 +362,15 @@ public ImageResource getImageResource(String uri) {
345362
* @return An XMLResource containing the image.
346363
*/
347364
@Override
348-
public XMLResource getXMLResource(String uri) {
365+
public XMLResource getXMLResource(String uri, ExternalResourceType type) {
366+
if (!checkAccessAllowed(uri, type, ExternalResourceControlPriority.RUN_BEFORE_RESOLVING_URI)) {
367+
return null;
368+
}
349369
String resolved = _resolver.resolveURI(this._baseUri, uri);
350-
370+
if (!checkAccessAllowed(resolved, type, ExternalResourceControlPriority.RUN_AFTER_RESOLVING_URI)) {
371+
return null;
372+
}
373+
351374
if (resolved == null) {
352375
XRLog.log(Level.INFO, LogMessageId.LogMessageId2Param.LOAD_URI_RESOLVER_REJECTED_LOADING_AT_URI, "XML resource", uri);
353376
return null;
@@ -363,9 +386,15 @@ public XMLResource getXMLResource(String uri) {
363386
}
364387

365388
@Override
366-
public byte[] getBinaryResource(String uri) {
389+
public byte[] getBinaryResource(String uri, ExternalResourceType type) {
390+
if (!checkAccessAllowed(uri, type, ExternalResourceControlPriority.RUN_BEFORE_RESOLVING_URI)) {
391+
return null;
392+
}
367393
String resolved = _resolver.resolveURI(this._baseUri, uri);
368-
394+
if (!checkAccessAllowed(resolved, type, ExternalResourceControlPriority.RUN_AFTER_RESOLVING_URI)) {
395+
return null;
396+
}
397+
369398
if (resolved == null) {
370399
XRLog.log(Level.INFO, LogMessageId.LogMessageId2Param.LOAD_URI_RESOLVER_REJECTED_LOADING_AT_URI, "binary resource", uri);
371400
return null;
@@ -422,6 +451,61 @@ public void setBaseURL(String uri) {
422451
_baseUri = uri;
423452
}
424453

454+
public static class DefaultAccessController
455+
implements BiPredicate<String, ExternalResourceType> {
456+
457+
public boolean test(String uri, ExternalResourceType resourceType) {
458+
if (resourceType == null) {
459+
return false;
460+
}
461+
462+
switch (resourceType) {
463+
case BINARY:
464+
case CSS:
465+
case FONT:
466+
case IMAGE_RASTER:
467+
case XML_XHTML:
468+
case XML_SVG:
469+
case PDF:
470+
case SVG_BINARY:
471+
return true;
472+
case FILE_EMBED:
473+
return false;
474+
}
475+
476+
return false;
477+
}
478+
}
479+
480+
public void setAccessController(
481+
ExternalResourceControlPriority prio,
482+
BiPredicate<String, ExternalResourceType> controller) {
483+
this._accessControllers.put(prio, controller);
484+
}
485+
486+
public boolean checkAccessAllowed(
487+
String uriOrResolved,
488+
ExternalResourceType type,
489+
ExternalResourceControlPriority priority) {
490+
BiPredicate<String, ExternalResourceType> controller = this._accessControllers.get(priority);
491+
492+
if (uriOrResolved == null) {
493+
return false;
494+
}
495+
496+
if (controller == null) {
497+
return true;
498+
}
499+
500+
boolean passed = controller.test(uriOrResolved, type);
501+
502+
if (!passed) {
503+
XRLog.log(Level.WARNING, LogMessageId.LogMessageId2Param.LOAD_RESOURCE_ACCESS_REJECTED, uriOrResolved, type);
504+
}
505+
506+
return passed;
507+
}
508+
425509
public static class DefaultUriResolver implements FSUriResolver {
426510
/**
427511
* Resolves the URI; if absolute, leaves as is, if relative, returns an

openhtmltopdf-core/src/main/java/com/openhtmltopdf/util/LogMessageId.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,7 @@ enum LogMessageId2Param implements LogMessageId {
217217
LOAD_URI_RESOLVER_REJECTED_LOADING_AT_URI(XRLog.LOAD, "URI resolver rejected loading {} at ({})"),
218218
LOAD_COULD_NOT_READ_URI_AT_URL_MAY_BE_RELATIVE(XRLog.LOAD, "Could not read {} as a URL; may be relative. Testing using parent URL {}"),
219219
LOAD_WAS_ABLE_TO_READ_FROM_URI_USING_PARENT_URL(XRLog.LOAD, "Was able to read from {} using parent URL {}"),
220+
LOAD_RESOURCE_ACCESS_REJECTED(XRLog.LOAD, "URI {} with type {} was rejected by resource access controller"),
220221

221222
GENERAL_FATAL_INFINITE_LOOP_BUG_IN_LINE_BREAKING_ALGO(XRLog.GENERAL, "A fatal infinite loop bug was detected in the line breaking " +
222223
"algorithm for break-word! Start-substring=[{}], end={}"),
Binary file not shown.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<html>
2+
<head>
3+
<style>
4+
@page {
5+
size: 200px 200px;
6+
}
7+
</style>
8+
<link rel="stylesheet" href="stylesheets/basic.css" />
9+
</head>
10+
<body>
11+
<div id="one">RED</div>
12+
<div id="two">BLUE</div>
13+
</body>
14+
</html>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
div#one { color: red; }

0 commit comments

Comments
 (0)