-
Notifications
You must be signed in to change notification settings - Fork 65
Added properties for base64 formatting #504
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
How about also removing org.apache.xml.security.utils.Base64? Is it needed anymore? |
seanjmullan
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some comments on a first pass.
| * DOM and XML accessibility and comfort functions. | ||
| * | ||
| * @implNote | ||
| * Following system properties affect XML formatting: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: "The following ..."
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed in 8a598d0, thank you
| import java.util.Iterator; | ||
| import java.util.List; | ||
| import java.util.Map; | ||
| import java.util.*; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you revert this change? I think it is better not to wildcard imports.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops, my IDE did it :) reverted
| return ignoreLineBreaks; | ||
| } | ||
|
|
||
| public void setIgnoreLineBreaks(boolean ignoreLineBreaks) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of set methods, why not add a constructor that takes the options and also checks for illegal syntax?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we would like to retain meaningful log messages for invalid property values, I couldn't find a better way than to move all properties reading together with validation logic to the constructor of Base64FormattingOptions.
Added warnings in case a property is set, but ignored.
Added handling of NumberFormatException as suggested in the comment below.
Example log:
testIgnoreLineBreaksTakesPrecedence:
... org.apache.xml.security.ignoreLineBreaks property takes precedence over org.apache.xml.security.base64.ignoreLineBreaks, line breaks will be ignored
... Property org.apache.xml.security.base64.lineSeparator has no effect since line breaks are ignored
... Property org.apache.xml.security.base64.lineLength has no effect since line breaks are ignored
testIllegalPropertiesAreIgnored:
... Illegal value of org.apache.xml.security.base64.lineSeparator property is ignored: illegal
... Illegal value of org.apache.xml.security.base64.lineLength property is ignored: illegal
| (PrivilegedAction<Boolean>) () -> Boolean.getBoolean("org.apache.xml.security.ignoreLineBreaks")); | ||
| (PrivilegedAction<Boolean>) () -> Boolean.getBoolean(IGNORE_LINE_BREAKS_PROP)); | ||
|
|
||
| private static Base64FormattingOptions base64Formatting = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if org.apache.xml.security.ignoreLineBreaks is true, none of these options matter, so did you consider skipping the getting of the other properties?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also should log a warning if the other properties are set, since they have no impact if org.apache.xml.security.ignoreLineBreaks is true.
| } | ||
|
|
||
| private static final Logger LOG = System.getLogger(XMLUtils.class.getName()); | ||
| Integer lineLength = Integer.getInteger(BASE64_LINE_LENGTH_PROP); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be better to use System.getProperty and then Integer.valueOf(String) because Integer.getInteger does not distinguish between the property not being set and an invalid value for the integer.
| /** | ||
| * Creates new formatting options by reading system properties. | ||
| */ | ||
| public Base64FormattingOptions() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The constructors and methods of this class should be package-private since the class is package-private.
| this.bytes = bytes; | ||
| } | ||
|
|
||
| public byte[] getBytes() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Change to package-private.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing Apache license header.
| import static org.hamcrest.MatcherAssert.assertThat; | ||
| import static org.hamcrest.Matchers.*; | ||
| import static org.junit.jupiter.api.Assertions.*; | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add a comment describing what this test does.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you consider testing the properties by generating signed XML and then checking the format? This test won't catch issues where the internal methods are not called accidentally.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The fact that XMLUtils is a utility class with static methods, and the configuration can only be done with system properties at the class load time makes it hard to test. That's why the test contains custom class loader which reloads the class in each test, allowing to run static fields initialization in the middle of the test.
To build a sort of integration test against public API e.g. XMLSignature, I believe we will need to load XMLSignature and all the classes it depends on with this custom classloader. There are additional considerations regarding Jigsaw module system: since the new classloader loads classes in its own module, all packages from xmlsec which are not exported cannot be used by this classloader. I can try to implement such a test, but I'm afraid it will be totally unreadable...
Intuitively, a good test interacts with the code in a way it is intended to be used. In our case, parts of the public interface are system properties. It looks proper to run the test suite with different JVMs started with different properties. Probably, Maven has something to achieve this, I'll investigate.
On the other hand, we can make it more testable (and probably a little more convenient for usage) if we provide a way to set Base64 options in runtime, for example:
- refer to system properties and resolve the options each time we do base64 encoding (e.g. inside
encodeToStringetc.) - add public setters for
base64FormattingOptionsandignoreLineBreaks - add public setter for
base64Encoder- then we don't need new options at all. We can default toBase64.getEncoder()/Base64.getMimeEncoder()depending onignoreLineBreaks, and if the clients need custom formatting - they just provide any implementation ofBase64.Encoder
However, I'm not aware of security implications behind this...
All other inline comments were addressed in 8a598d0. Thank you for checking the code carefully.
New feature
This PR continues the mailing list thread and suggests system properties to control base64Binary values formatting:
org.apache.xml.security.base64.lineSeparator,org.apache.xml.security.base64.lineLength- allows to override default MIME encoding settingsorg.apache.xml.security.base64.ignoreLineBreaks- disables line wrapping for base64 value, but allows to keep the whole XML pretty-printed. Takes precedence over line wrapping settings above.org.apache.xml.security.ignoreLineBreakstakes precedence over all new options.Default values result to CRLF line breaks with 76 chars in the line, making the whole thing fully compatible with previous implementation.
Other changes
Some of the logic is included to XMLUtils to provide better encapsulation:
XMLUtils.encodeElementValueBase64OutputStreamfrom Commons, used in XML encryption, is replaced with ajava.utilimplementation and provided byXMLUtils, making it consistent with configured Base64 encoder.Base64.Encoder/Decoderinstances are thread-safe, so sharing a single instance should give us a little performance gain.Test coverage
As the base64 configuration takes place during class load, unit-tests leverage from custom
ClassLoaderand Reflection API to reinitialize the class in each test.Motivation
https://www.w3.org/TR/xmlschema-2/#base64Binary -
base64Binarydefinition has note, that original RFC2045 line-length limitation must not be enforced. In current implementation it is only possible to remove all line breaks in the document usingorg.apache.xml.security.ignoreLineBreaks, sacrificing human readability.Also, using LF instead of CRLF may be desired in systems where verifying side does not expect escape sequences in base64Binary values.
I would be thankful for your comments and suggestions.