use of org.structr.web.common.RenderContext in project structr by structr.
the class HtmlServlet method doGet.
@Override
protected void doGet(final HttpServletRequest request, final HttpServletResponse response) {
final Authenticator auth = getConfig().getAuthenticator();
List<Page> pages = null;
boolean requestUriContainsUuids = false;
SecurityContext securityContext;
final App app;
try {
assertInitialized();
final String path = request.getPathInfo() != null ? request.getPathInfo() : "/";
// check for registration (has its own tx because of write access
if (checkRegistration(auth, request, response, path)) {
return;
}
// check for registration (has its own tx because of write access
if (checkResetPassword(auth, request, response, path)) {
return;
}
// isolate request authentication in a transaction
try (final Tx tx = StructrApp.getInstance().tx()) {
securityContext = auth.initializeAndExamineRequest(request, response);
tx.success();
} catch (AuthenticationException aex) {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
return;
}
app = StructrApp.getInstance(securityContext);
try (final Tx tx = app.tx()) {
// Ensure access mode is frontend
securityContext.setAccessMode(AccessMode.Frontend);
request.setCharacterEncoding("UTF-8");
// Important: Set character encoding before calling response.getWriter() !!, see Servlet Spec 5.4
response.setCharacterEncoding("UTF-8");
boolean dontCache = false;
logger.debug("Path info {}", path);
// don't continue on redirects
if (response.getStatus() == 302) {
tx.success();
return;
}
final Principal user = securityContext.getUser(false);
if (user != null) {
// Don't cache if a user is logged in
dontCache = true;
}
final RenderContext renderContext = RenderContext.getInstance(securityContext, request, response);
renderContext.setResourceProvider(config.getResourceProvider());
final EditMode edit = renderContext.getEditMode(user);
DOMNode rootElement = null;
AbstractNode dataNode = null;
final String[] uriParts = PathHelper.getParts(path);
if ((uriParts == null) || (uriParts.length == 0)) {
// find a visible page
rootElement = findIndexPage(securityContext, pages, edit);
logger.debug("No path supplied, trying to find index page");
} else {
if (rootElement == null) {
rootElement = findPage(securityContext, pages, path, edit);
} else {
dontCache = true;
}
}
if (rootElement == null) {
// No page found
// In case of a file, try to find a file with the query string in the filename
final String queryString = request.getQueryString();
// Look for a file, first include the query string
File file = findFile(securityContext, request, path + (queryString != null ? "?" + queryString : ""));
// If no file with query string in the file name found, try without query string
if (file == null) {
file = findFile(securityContext, request, path);
}
if (file != null) {
streamFile(securityContext, file, request, response, edit);
tx.success();
return;
}
if (uriParts != null) {
// store remaining path parts in request
final Matcher matcher = threadLocalUUIDMatcher.get();
for (int i = 0; i < uriParts.length; i++) {
request.setAttribute(uriParts[i], i);
matcher.reset(uriParts[i]);
// set to "true" if part matches UUID pattern
requestUriContainsUuids |= matcher.matches();
}
}
if (!requestUriContainsUuids) {
// Try to find a data node by name
dataNode = findFirstNodeByName(securityContext, request, path);
} else {
dataNode = findNodeByUuid(securityContext, PathHelper.getName(path));
}
// if (dataNode != null && !(dataNode instanceof Linkable)) {
if (dataNode != null) {
// Last path part matches a data node
// Remove last path part and try again searching for a page
// clear possible entry points
request.removeAttribute(POSSIBLE_ENTRY_POINTS_KEY);
rootElement = findPage(securityContext, pages, StringUtils.substringBeforeLast(path, PathHelper.PATH_SEP), edit);
renderContext.setDetailsDataObject(dataNode);
// Start rendering on data node
if (rootElement == null && dataNode instanceof DOMNode) {
// check visibleForSite here as well
if (!(dataNode instanceof Page) || isVisibleForSite(request, (Page) dataNode)) {
rootElement = ((DOMNode) dataNode);
}
}
}
}
// look for pages with HTTP Basic Authentication (must be done as superuser)
if (rootElement == null) {
final HttpBasicAuthResult authResult = checkHttpBasicAuth(request, response, path);
switch(authResult.authState()) {
// Element with Basic Auth found and authentication succeeded
case Authenticated:
final Linkable result = authResult.getRootElement();
if (result instanceof Page) {
rootElement = (DOMNode) result;
securityContext = authResult.getSecurityContext();
renderContext.pushSecurityContext(securityContext);
} else if (result instanceof File) {
streamFile(authResult.getSecurityContext(), (File) result, request, response, EditMode.NONE);
tx.success();
return;
}
break;
// Page with Basic Auth found but not yet authenticated
case MustAuthenticate:
final Page errorPage = StructrApp.getInstance().nodeQuery(Page.class).and(StructrApp.key(Page.class, "showOnErrorCodes"), "401", false).getFirst();
if (errorPage != null && isVisibleForSite(request, errorPage)) {
// set error page
rootElement = errorPage;
// don't cache the error page
dontCache = true;
} else {
// send error
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
tx.success();
return;
}
break;
// no Basic Auth for given path, go on
case NoBasicAuth:
break;
}
}
// Still nothing found, do error handling
if (rootElement == null) {
rootElement = notFound(response, securityContext);
}
if (rootElement == null) {
tx.success();
return;
}
// check dont cache flag on page (if root element is a page)
// but don't modify true to false
dontCache |= rootElement.dontCache();
if (EditMode.WIDGET.equals(edit) || dontCache) {
setNoCacheHeaders(response);
}
if (!securityContext.isVisible(rootElement)) {
rootElement = notFound(response, securityContext);
if (rootElement == null) {
tx.success();
return;
}
} else {
if (!EditMode.WIDGET.equals(edit) && !dontCache && notModifiedSince(request, response, rootElement, dontCache)) {
ServletOutputStream out = response.getOutputStream();
out.flush();
// response.flushBuffer();
out.close();
} else {
// prepare response
response.setCharacterEncoding("UTF-8");
String contentType = rootElement.getProperty(StructrApp.key(Page.class, "contentType"));
if (contentType == null) {
// Default
contentType = "text/html;charset=UTF-8";
}
if (contentType.equals("text/html")) {
contentType = contentType.concat(";charset=UTF-8");
}
response.setContentType(contentType);
setCustomResponseHeaders(response);
final boolean createsRawData = rootElement.getProperty(StructrApp.key(Page.class, "pageCreatesRawData"));
// async or not?
if (isAsync && !createsRawData) {
final AsyncContext async = request.startAsync();
final ServletOutputStream out = async.getResponse().getOutputStream();
final AtomicBoolean finished = new AtomicBoolean(false);
final DOMNode rootNode = rootElement;
threadPool.submit(new Runnable() {
@Override
public void run() {
try (final Tx tx = app.tx()) {
// render
rootNode.render(renderContext, 0);
finished.set(true);
tx.success();
} catch (Throwable t) {
t.printStackTrace();
logger.warn("Error while rendering page {}: {}", rootNode.getName(), t.getMessage());
try {
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
finished.set(true);
} catch (IOException ex) {
logger.warn("", ex);
}
}
}
});
// start output write listener
out.setWriteListener(new WriteListener() {
@Override
public void onWritePossible() throws IOException {
try {
final Queue<String> queue = renderContext.getBuffer().getQueue();
while (out.isReady()) {
String buffer = null;
synchronized (queue) {
buffer = queue.poll();
}
if (buffer != null) {
out.print(buffer);
} else {
if (finished.get()) {
async.complete();
// prevent this block from being called again
break;
}
Thread.sleep(1);
}
}
} catch (Throwable t) {
logger.warn("", t);
}
}
@Override
public void onError(Throwable t) {
logger.warn("", t);
}
});
} else {
final StringRenderBuffer buffer = new StringRenderBuffer();
renderContext.setBuffer(buffer);
// render
rootElement.render(renderContext, 0);
try {
response.getOutputStream().write(buffer.getBuffer().toString().getBytes("utf-8"));
response.getOutputStream().flush();
response.getOutputStream().close();
} catch (IOException ioex) {
logger.warn("", ioex);
}
}
}
}
tx.success();
} catch (FrameworkException fex) {
logger.error("Exception while processing request: {}", fex.getMessage());
}
} catch (FrameworkException fex) {
logger.error("Exception while processing request: {}", fex.getMessage());
UiAuthenticator.writeFrameworkException(response, fex);
} catch (IOException ioex) {
logger.error("Exception while processing request: {}", ioex.getMessage());
UiAuthenticator.writeInternalServerError(response);
}
}
use of org.structr.web.common.RenderContext in project structr by structr.
the class Page method getContent.
static String getContent(final Page page, final RenderContext.EditMode editMode) throws FrameworkException {
final RenderContext ctx = new RenderContext(page.getSecurityContext(), null, null, editMode);
final StringRenderBuffer buffer = new StringRenderBuffer();
ctx.setBuffer(buffer);
page.render(ctx, 0);
// extract source
return buffer.getBuffer().toString();
}
use of org.structr.web.common.RenderContext in project structr by structr.
the class IncludeFunction method apply.
@Override
public Object apply(final ActionContext ctx, final Object caller, final Object[] sources) throws FrameworkException {
try {
if (!(arrayHasMinLengthAndAllElementsNotNull(sources, 1) && sources[0] instanceof String)) {
return null;
}
final PropertyKey<DOMNode> sharedCompKey = StructrApp.key(DOMNode.class, "sharedComponent");
final SecurityContext securityContext = ctx.getSecurityContext();
final App app = StructrApp.getInstance(securityContext);
final RenderContext innerCtx = new RenderContext((RenderContext) ctx);
final List<DOMNode> nodeList = app.nodeQuery(DOMNode.class).andName((String) sources[0]).getAsList();
DOMNode node = null;
/**
* Nodes can be included via their name property These nodes MUST: 1. be unique in name 2. NOT be in the trash => have an ownerDocument AND a parent (public
* users are not allowed to see the __ShadowDocument__ ==> this check must either be made in a superuser-context OR the __ShadowDocument could be made public?)
*
* These nodes can be: 1. somewhere in the pages tree 2. in the shared components 3. both ==> causes a problem because we now have multiple nodes with the same
* name (one shared component and multiple linking instances of that component)
*
* INFOS:
*
* - If a DOMNode has "syncedNodes" it MUST BE a shared component - If a DOMNodes "sharedComponent" is set it MUST BE AN INSTANCE of a shared component => Can
* we safely ignore these? I THINK SO!
*/
for (final DOMNode n : nodeList) {
if (n.inTrash()) {
continue;
}
// IGNORE everything that REFERENCES a shared component!
if (n.getProperty(sharedCompKey) == null) {
// the DOMNode is either a shared component OR a named node in the pages tree
if (node == null) {
node = n;
} else {
// TODO: Do we need to remove the nodes from the nodeList which can be ignored? (references to a shared component)
return "Ambiguous node name \"" + ((String) sources[0]) + "\" (nodes found: " + StringUtils.join(nodeList, ", ") + ")";
}
}
}
if (node != null) {
if (sources.length == 3 && sources[1] instanceof Iterable && sources[2] instanceof String) {
final Iterable<GraphObject> iterable = FunctionDataSource.map((Iterable) sources[1]);
final String dataKey = (String) sources[2];
innerCtx.setListSource(iterable);
node.renderNodeList(securityContext, innerCtx, 0, dataKey);
} else {
node.render(innerCtx, 0);
}
if (innerCtx.appLibRendered()) {
((RenderContext) ctx).setAppLibRendered(true);
}
} else {
final File file = app.nodeQuery(File.class).andName((String) sources[0]).getFirst();
if (file != null) {
final String name = file.getProperty(NodeInterface.name);
final String contentType = file.getContentType();
final String charset = StringUtils.substringAfterLast(contentType, "charset=");
final String extension = StringUtils.substringAfterLast(name, ".");
if (contentType == null || StringUtils.isBlank(extension)) {
logger.warn("No valid file type detected. Please make sure {} has a valid content type set or file extension. Parameters: {}", new Object[] { name, getParametersAsString(sources) });
return "No valid file type detected. Please make sure " + name + " has a valid content type set or file extension.";
}
if (contentType.startsWith("text/css")) {
return "<link href=\"" + file.getPath() + "\" rel=\"stylesheet\">";
} else if (contentType.contains("/javascript")) {
return "<script src=\"" + file.getPath() + "\"></script>";
} else if (contentType.startsWith("image/svg")) {
try (final InputStream is = file.getInputStream()) {
final byte[] buffer = new byte[file.getSize().intValue()];
IOUtils.read(is, buffer);
return StringUtils.toEncodedString(buffer, Charset.forName(charset));
} catch (IOException ex) {
logger.warn("Exception for parameters: {}", getParametersAsString(sources));
logger.error("", ex);
}
return "<img alt=\"" + name + "\" src=\"" + file.getPath() + "\">";
} else if (contentType.startsWith("image/")) {
return "<img alt=\"" + name + "\" src=\"" + file.getPath() + "\">";
} else {
logger.warn("Don't know how to render content type or extension of {}. Parameters: {}", new Object[] { name, getParametersAsString(sources) });
return "Don't know how to render content type or extension of " + name + ".";
}
}
}
return StringUtils.join(innerCtx.getBuffer().getQueue(), "");
} catch (final IllegalArgumentException e) {
logParameterError(caller, sources, ctx.isJavaScriptContext());
return usage(ctx.isJavaScriptContext());
}
}
use of org.structr.web.common.RenderContext in project structr by structr.
the class UiScriptingTest method testScripting.
@Test
public void testScripting() {
NodeInterface detailsDataObject = null;
Page page = null;
DOMNode html = null;
DOMNode head = null;
DOMNode body = null;
DOMNode title = null;
DOMNode div = null;
DOMNode p = null;
DOMNode text = null;
try (final Tx tx = app.tx()) {
detailsDataObject = app.create(TestOne.class, "TestOne");
page = Page.createNewPage(securityContext, "testpage");
page.setProperties(page.getSecurityContext(), new PropertyMap(Page.visibleToPublicUsers, true));
assertTrue(page != null);
assertTrue(page instanceof Page);
html = (DOMNode) page.createElement("html");
head = (DOMNode) page.createElement("head");
body = (DOMNode) page.createElement("body");
title = (DOMNode) page.createElement("title");
div = (DOMNode) page.createElement("div");
p = (DOMNode) page.createElement("p");
text = (DOMNode) page.createTextNode("x");
// add HTML element to page
page.appendChild(html);
// add HEAD and BODY elements to HTML
html.appendChild(head);
html.appendChild(body);
// add TITLE element to HEAD
head.appendChild(title);
body.appendChild(div);
div.appendChild(p);
final PropertyMap changedProperties = new PropertyMap();
changedProperties.put(StructrApp.key(DOMElement.class, "restQuery"), "/divs");
changedProperties.put(StructrApp.key(DOMElement.class, "dataKey"), "div");
p.setProperties(p.getSecurityContext(), changedProperties);
p.appendChild(text);
tx.success();
} catch (FrameworkException fex) {
fail("Unexpected exception");
}
try (final Tx tx = app.tx()) {
final RenderContext ctx = new RenderContext(securityContext, new RequestMockUp(), new ResponseMockUp(), RenderContext.EditMode.NONE);
ctx.setDetailsDataObject(detailsDataObject);
ctx.setPage(page);
test(p, text, "${{ return Structr.get('div').id}}", "<p>" + div.getUuid() + "</p>", ctx);
test(p, text, "${{ return Structr.get('page').id}}", "<p>" + page.getUuid() + "</p>", ctx);
test(p, text, "${{ return Structr.get('parent').id}}", "<p>" + p.getUuid() + "</p>", ctx);
tx.success();
} catch (FrameworkException fex) {
logger.warn("", fex);
fail("Unexpected exception.");
}
}
use of org.structr.web.common.RenderContext in project structr by structr.
the class UiScriptingTest method testDoPrivileged.
@Test
public void testDoPrivileged() {
User tester = null;
try (final Tx tx = app.tx()) {
// create admin user
createTestNode(User.class, new NodeAttribute<>(StructrApp.key(User.class, "name"), "admin"), new NodeAttribute<>(StructrApp.key(User.class, "password"), "admin"), new NodeAttribute<>(StructrApp.key(User.class, "isAdmin"), true));
// create test user
tester = createTestNode(User.class, new NodeAttribute<>(StructrApp.key(User.class, "name"), "tester"), new NodeAttribute<>(StructrApp.key(User.class, "password"), "test"));
tx.success();
} catch (FrameworkException fex) {
fex.printStackTrace();
fail("Unexpected exception.");
}
final String script1 = "${{ return Structr.find('User', 'name', 'admin'); }}\n";
final String script2 = "${{ return Structr.doPrivileged(function() { return Structr.find('User', 'name', 'admin'); }); }}\n";
final SecurityContext userContext = SecurityContext.getInstance(tester, AccessMode.Backend);
final App app = StructrApp.getInstance(userContext);
final RenderContext renderContext = new RenderContext(userContext, new RequestMockUp(), new ResponseMockUp(), RenderContext.EditMode.NONE);
try (final Tx tx = app.tx()) {
// unprivileged call
final Object result = Scripting.evaluate(renderContext, null, script1, "test");
assertEquals("Result is of invalid type", ArrayList.class, result.getClass());
assertEquals("Script in user context should not see admin", 0, ((List) result).size());
tx.success();
} catch (FrameworkException fex) {
fex.printStackTrace();
fail("Unexpected exception.");
}
try (final Tx tx = app.tx()) {
// doPrivileged call
final Object result = Scripting.evaluate(renderContext, null, script2, "test");
assertEquals("Result is of invalid type", ArrayList.class, result.getClass());
assertEquals("Privileged script should not see admin", 1, ((List) result).size());
tx.success();
} catch (FrameworkException fex) {
fex.printStackTrace();
fail("Unexpected exception.");
}
}
Aggregations