HtmlTemplates.java
/*
* Copyright 2004-present 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 org.springframework.security.web.webauthn.registration;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.util.HtmlUtils;
/**
* Render HTML templates using string substitution. Intended for internal use. Variables
* can be templated using double curly-braces: {@code {{name}}}.
*
* @author Daniel Garnier-Moiroux
* @since 6.4
* @see org.springframework.security.web.authentication.ui.HtmlTemplates
*/
final class HtmlTemplates {
private HtmlTemplates() {
}
static HtmlTemplates.Builder fromTemplate(String template) {
return new HtmlTemplates.Builder(template);
}
static final class Builder {
private final String template;
private final Map<String, String> values = new HashMap<>();
private Builder(String template) {
this.template = template;
}
/**
* HTML-escape, and inject value {@code value} in every {@code {{key}}}
* placeholder.
* @param key the placeholder name
* @param value the value to inject
* @return this instance for further templating
*/
HtmlTemplates.Builder withValue(String key, Object value) {
Assert.notNull(value, "value cannot be null");
this.values.put(key, HtmlUtils.htmlEscape(value.toString()));
return this;
}
/**
* Inject value {@code value} in every {@code {{key}}} placeholder without
* HTML-escaping. Useful for injecting "sub-templates".
* @param key the placeholder name
* @param value the value to inject
* @return this instance for further templating
*/
HtmlTemplates.Builder withRawHtml(String key, String value) {
if (!value.isEmpty() && value.charAt(value.length() - 1) == '\n') {
value = value.substring(0, value.length() - 1);
}
this.values.put(key, value);
return this;
}
/**
* Render the template. All placeholders MUST have a corresponding value. If a
* placeholder does not have a corresponding value, throws
* {@link IllegalStateException}.
* @return the rendered template
*/
String render() {
String template = this.template;
for (String key : this.values.keySet()) {
String pattern = Pattern.quote("{{" + key + "}}");
template = template.replaceAll(pattern, this.values.get(key));
}
String unusedPlaceholders = Pattern.compile("\\{\\{([a-zA-Z0-9]+)}}")
.matcher(template)
.results()
.map((result) -> result.group(1))
.collect(Collectors.joining(", "));
if (StringUtils.hasLength(unusedPlaceholders)) {
throw new IllegalStateException("Unused placeholders in template: [%s]".formatted(unusedPlaceholders));
}
return template;
}
}
}