/*
* #%L
* Wisdom-Framework
* %%
* Copyright (C) 2013 - 2015 Wisdom Framework
* %%
* 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
*
* http://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.
* #L%
*/
package org.wisdom.framework.csrf;
import com.google.common.collect.ImmutableSet;
import org.apache.felix.ipojo.annotations.*;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;
import org.thymeleaf.Arguments;
import org.thymeleaf.dialect.AbstractDialect;
import org.thymeleaf.dialect.IDialect;
import org.thymeleaf.dom.Element;
import org.thymeleaf.dom.Node;
import org.thymeleaf.processor.IProcessor;
import org.thymeleaf.processor.element.AbstractMarkupSubstitutionElementProcessor;
import org.wisdom.api.templates.TemplateEngine;
import org.wisdom.framework.csrf.api.CSRFService;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
/**
* Exposed the csrf dialect for thymeleaf injecting a hidden input field in form containing a CSRF token. As this is
* only available for Thmeleaf, this component publishes the dialect only if the Thymeleaf template engine is available.
*/
@Component(immediate = true)
@Instantiate
public class ThymeleafCsrfDialect {
@Context
BundleContext context;
@Requires
CSRFService csrf;
ServiceRegistration<IDialect> reg;
@Bind(aggregate = true)
public void bindTemplateEngine(TemplateEngine engine) {
if (engine.name().equals("thymeleaf")) {
publishDialect();
}
}
private void publishDialect() {
reg = context.registerService(IDialect.class, createDialect(), null);
}
protected IDialect createDialect() {
return new AbstractDialect() {
@Override
public String getPrefix() {
return "csrf";
}
@Override
public Set<IProcessor> getProcessors() {
return ImmutableSet.<IProcessor>of(new CSRFElementProcessor());
}
};
}
@Unbind
public void unbindTemplateEngine(TemplateEngine engine) {
if (engine.name().equals("thymeleaf")) {
unpublishDialect();
}
}
private void unpublishDialect() {
if (reg != null) {
reg.unregister();
reg = null;
}
}
class CSRFElementProcessor extends AbstractMarkupSubstitutionElementProcessor {
public CSRFElementProcessor() {
super("token");
}
@Override
public int getPrecedence() {
return 1000;
}
@Override
protected List<Node> getMarkupSubstitutes(
final Arguments arguments, final Element element) {
final Element input = new Element("input");
input.setAttribute("type", "hidden");
input.setAttribute("name", csrf.getTokenName());
final String token = csrf.getCurrentToken(org.wisdom.api.http.Context.CONTEXT.get());
if (token != null) {
input.setAttribute("value", token);
} else {
input.setAttribute("value", "invalid");
}
final List<Node> nodes = new ArrayList<>();
nodes.add(input);
return nodes;
}
}
}