Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

<groupId>fr.wseduc</groupId>
<artifactId>web-utils</artifactId>
<version>3.2-SNAPSHOT</version>
<version>3.2-develop-b2school-SNAPSHOT</version>

<repositories>
<repository>
Expand Down
117 changes: 117 additions & 0 deletions src/main/java/fr/wseduc/webutils/http/ProcessTemplateContext.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package fr.wseduc.webutils.http;

import com.samskivert.mustache.Mustache;
import io.vertx.core.http.HttpServerRequest;
import io.vertx.core.json.JsonObject;

import java.io.Reader;
import java.util.HashMap;
import java.util.Map;

/**
* Container for all parameters needed for the template processing
*/
public final class ProcessTemplateContext {

private final HttpServerRequest request;
private final JsonObject params;
private final String templateString;
private final Reader reader;
private final boolean escapeHtml;
private final String defaultValue;
private final Map<String, Mustache.Lambda> lambdas;

private ProcessTemplateContext(HttpServerRequest request, JsonObject params, String templateString, Reader reader,
boolean escapeHtml, String defaultValue, Map<String, Mustache.Lambda> lambdas) {
this.request = request;
this.params = params;
this.templateString = templateString;
this.reader = reader;
this.escapeHtml = escapeHtml;
this.defaultValue = defaultValue;
this.lambdas = new HashMap<>(lambdas);
}

public static class Builder {

private HttpServerRequest request;
private JsonObject params;
private String templateString;
private Reader reader;
private boolean escapeHtml;
private String defaultValue;
private final Map<String, Mustache.Lambda> lambdas = new HashMap<>();

public Builder request(HttpServerRequest request) {
this.request = request;
return this;
}

public Builder escapeHtml(boolean escapeHtml) {
this.escapeHtml = escapeHtml;
return this;
}

public Builder setDefaultValue(String defaultValue) {
this.defaultValue = defaultValue;
return this;
}

public Builder params(JsonObject params) {
this.params = params;
return this;
}

public Builder templateString(String templateString) {
this.templateString = templateString;
return this;
}

public Builder reader(Reader reader) {
this.reader = reader;
return this;
}

public Builder lambdas(Map<String, Mustache.Lambda> lambdas) {
this.lambdas.putAll(lambdas);
return this;
}

public HttpServerRequest request() {
return request;
}

public ProcessTemplateContext build() {
return new ProcessTemplateContext(request, params, templateString, reader, escapeHtml, defaultValue, lambdas);
}

}

public HttpServerRequest request() {
return request;
}

public JsonObject params() {
return params;
}

public String templateString() {
return templateString;
}

public Reader reader() {
return reader;
}

public Map<String, Mustache.Lambda> lambdas() {
return lambdas;
}

public boolean escapeHtml() {
return escapeHtml;
}

public String defaultValue() {
return defaultValue;
}
}
143 changes: 127 additions & 16 deletions src/main/java/fr/wseduc/webutils/http/Renders.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,11 @@
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.samskivert.mustache.Mustache;
import fr.wseduc.webutils.template.TemplateProcessor;
import fr.wseduc.webutils.template.FileTemplateProcessor;
import fr.wseduc.webutils.template.lambdas.FormatBirthDateLambda;
Expand All @@ -30,6 +33,7 @@
import fr.wseduc.webutils.template.lambdas.LocaleDateLambda;
import fr.wseduc.webutils.template.lambdas.ModsLambda;
import fr.wseduc.webutils.template.lambdas.StaticLambda;
import io.micrometer.common.util.StringUtils;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.vertx.core.Handler;
import io.vertx.core.Vertx;
Expand All @@ -47,6 +51,12 @@

import static fr.wseduc.webutils.Utils.isNotEmpty;

/**
* Method with @deprecated, relative to template processing are NOT thread safe and should not be used. Alternative method not deprecated was developed.
* Lambda for template processing dependent of the user context (request, host, etc..) must not be shared and are local variable.
* Static lambda that doesn't change with the context can be used
* Parameter for the template are either NOT thread safe. EscapeHTML, defaultValue, should be passed as local variable
*/
public class Renders {

protected static final Logger log = LoggerFactory.getLogger(Renders.class);
Expand All @@ -57,6 +67,7 @@ public class Renders {
protected String staticHost;
protected FileTemplateProcessor templateProcessor;
protected static final List<String> allowedHosts = new ArrayList<>();
private final Map<String, Mustache.Lambda> staticLambdas = new HashMap<>();

public Renders(Vertx vertx, JsonObject config) {
this.config = config;
Expand All @@ -66,8 +77,10 @@ public Renders(Vertx vertx, JsonObject config) {
this.vertx = vertx;
if (vertx != null) {
this.templateProcessor = new FileTemplateProcessor(vertx, "view/", false);
this.templateProcessor.setLambda("formatBirthDate", new FormatBirthDateLambda());
this.templateProcessor.setLambda("modVersion", new ModsLambda(vertx));
staticLambdas.put("formatBirthDate", new FormatBirthDateLambda());
staticLambdas.put("modVersion", new ModsLambda(vertx));

staticLambdas.forEach(templateProcessor::setLambda);
}
}

Expand All @@ -84,13 +97,15 @@ protected void init(Vertx vertx, JsonObject config)

if (templateProcessor == null && vertx != null) {
this.templateProcessor = new FileTemplateProcessor(vertx, "view/", false);
this.templateProcessor.setLambda("formatBirthDate", new FormatBirthDateLambda());
this.templateProcessor.setLambda("modVersion", new ModsLambda(vertx));
staticLambdas.put("formatBirthDate", new FormatBirthDateLambda());
staticLambdas.put("modVersion", new ModsLambda(vertx));

staticLambdas.forEach(templateProcessor::setLambda);
}
}

protected void setLambdaTemplateRequest(final HttpServerRequest request)
{
@Deprecated
protected void setLambdaTemplateRequest(final HttpServerRequest request) {
String host = Renders.getHost(request);
if(host == null) // This can happen for forged requests
host = "";
Expand All @@ -104,6 +119,88 @@ protected void setLambdaTemplateRequest(final HttpServerRequest request)
this.templateProcessor.setLambda("datetime", new LocaleDateLambda(I18n.acceptLanguage(request)));
}

/**
* Create lambda from request for mustache. These lambdas should be used with TemplateProcessorContext and method
* that use it in order to avoid race condition on template processing
* @param request HttpRequest of the user to set various dependent request lambda (host, domain, language, etc..)
* @return Map with request dependent lambda
*/
protected Map<String, Mustache.Lambda> getLambdasFromRequest(final HttpServerRequest request) {
Map<String, Mustache.Lambda> lambdas = new HashMap<>();

String host = Renders.getHost(request);
if(host == null) // This can happen for forged requests
host = "";
String sttcHost = this.staticHost != null ? this.staticHost : host;


lambdas.put("i18n",
new I18nLambda(I18n.acceptLanguage(request), host, I18n.getTheme(request)));
lambdas.put("static",
new StaticLambda(config.getBoolean("ssl", sttcHost.startsWith("https")), sttcHost, this.pathPrefix + "/public"));
lambdas.put("infra",
new InfraLambda(config.getBoolean("ssl", sttcHost.startsWith("https")), sttcHost, "/infra/public", request.headers().get("X-Forwarded-For") == null));
lambdas.put("datetime", new LocaleDateLambda(I18n.acceptLanguage(request)));
return lambdas;
}

public void renderTemplateView(HttpServerRequest request) {
renderTemplateView(request, new JsonObject(), new HashMap<>());
}

public void renderTemplateView(HttpServerRequest request, Map<String, Mustache.Lambda> lambdas) {
renderTemplateView(request, new JsonObject(), lambdas);
}

public void renderTemplateView(HttpServerRequest request, JsonObject params) {
renderTemplateView(request, params, new HashMap<>());
}

public void renderTemplateView(HttpServerRequest request, JsonObject params, Map<String, Mustache.Lambda> lambdas) {
renderTemplateView(request, params, null, null, 200, lambdas);
}

public void renderTemplateView(HttpServerRequest request, JsonObject params, String resourceName,
Reader r, Map<String, Mustache.Lambda> lambdas) {
renderTemplateView(request, params, resourceName, r, 200, lambdas);
}

public void renderTemplateView(final HttpServerRequest request, JsonObject params,
String resourceName, Reader r, final int status, Map<String, Mustache.Lambda> lambdas) {

Map<String, Mustache.Lambda> mergeLambdas = getLambdasFromRequest(request);
mergeLambdas.putAll(lambdas);
mergeLambdas.putAll(staticLambdas);

ProcessTemplateContext.Builder context = new ProcessTemplateContext.Builder()
.escapeHtml(true)
.reader(r)
.lambdas( mergeLambdas)
.request(request);

context.templateString(genTemplateName(resourceName, request));

templateProcessor.processTemplate(context.build(), writer -> {
if (writer != null) {
request.response().putHeader("content-type", "text/html; charset=utf-8");
request.response().setStatusCode(status);
if (hookRenderProcess != null) {
executeHandlersHookRender(request, new Handler<Void>() {
@Override
public void handle(Void v) {
request.response().end(writer.toString());
}
});
} else {
request.response().end(writer.toString());
}
} else {
renderError(request);
}
});
}

@Deprecated
public void renderView(HttpServerRequest request) {
renderView(request, new JsonObject());
}
Expand All @@ -112,14 +209,17 @@ public void renderView(HttpServerRequest request) {
* Render a Mustache template : see http://mustache.github.com/mustache.5.html
* TODO : isolate scope management
*/
@Deprecated
public void renderView(HttpServerRequest request, JsonObject params) {
renderView(request, params, null, null, 200);
}

@Deprecated
public void renderView(HttpServerRequest request, JsonObject params, String resourceName, Reader r) {
renderView(request, params, resourceName, r, 200);
}

@Deprecated
public void renderView(final HttpServerRequest request, JsonObject params,
String resourceName, Reader r, final int status) {
processTemplate(request, params, resourceName, r, new Handler<Writer>() {
Expand Down Expand Up @@ -161,37 +261,48 @@ public void handle(Void v) {
handlers[0].handle(null);
}

@Deprecated
public void processTemplate(HttpServerRequest request, String template, JsonObject params, final Handler<String> handler)
{
this.setLambdaTemplateRequest(request);
this.templateProcessor.escapeHTML(true).processTemplate(this.genTemplateName(template, request), params, handler);
}

@Deprecated
public void processTemplate(final HttpServerRequest request, JsonObject p, String resourceName, Reader r, final Handler<Writer> handler)
{
this.setLambdaTemplateRequest(request);
this.templateProcessor.escapeHTML(true);
this.templateProcessor.processTemplate(this.genTemplateName(resourceName, request), p, r, handler);
}

@Deprecated
public void processTemplate(final HttpServerRequest request, JsonObject p, String resourceName, boolean escapeHTML, final Handler<String> handler)
{
this.setLambdaTemplateRequest(request);
this.templateProcessor.escapeHTML(escapeHTML).processTemplate(this.genTemplateName(resourceName, request), p, handler);
}

private String genTemplateName(final String resourceName, final HttpServerRequest request)
{
if (resourceName != null && !resourceName.trim().isEmpty())
public void processTemplateWithLambdas(ProcessTemplateContext.Builder context, Handler<Writer> handler) {
context.lambdas(getLambdasFromRequest(context.request()));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

En passant par public void renderTemplateView(final HttpServerRequest request, JsonObject params, String resourceName, Reader r, final int status, Map<String, Mustache.Lambda> lambdas) { j'ai l'impression qu'on fait 2 fois le boulot de getLambdasFromRequest

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oui c'et vrai

this.templateProcessor.processTemplate(context.build(), handler);
}

public void processTemplateWithLambdas(ProcessTemplateContext.Builder context, String resourceName, Handler<Writer> handler) {
context.lambdas(getLambdasFromRequest(context.request()));
context.templateString(genTemplateName(resourceName, context.request()));
this.templateProcessor.processTemplate(context.build(), handler);
}

private String genTemplateName(final String resourceName, final HttpServerRequest request) {
if (!StringUtils.isEmpty(resourceName)) {
return resourceName;
else
{
String template = request.path().substring(pathPrefix.length());
if (template.trim().isEmpty()) {
template = pathPrefix.substring(1);
}
return template + ".html";
}
String template = request.path().substring(pathPrefix.length());
if (template.trim().isEmpty()) {
template = pathPrefix.substring(1);
}
return template + ".html";
}

public static void ok(HttpServerRequest request) {
Expand Down
8 changes: 8 additions & 0 deletions src/main/java/fr/wseduc/webutils/security/SecuredAction.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@ public SecuredAction(String qualifiedName, String displayName, String type, Stri
this.right = right;
}

public SecuredAction(String qualifiedName, String displayName, String type) {
this.name = qualifiedName;
this.displayName = displayName;
this.type = type;
this.right = qualifiedName;
}


public String getName() {
return name;
}
Expand Down
Loading