Commit 94c118d0 authored by Matija Obreza's avatar Matija Obreza
Browse files

Merge branch 'beanshell' into 'master'

Beanshell

See merge request genesys-pgr/genesys-server!116
parents 54a2b807 1dea75f5
......@@ -537,6 +537,11 @@
<artifactId>glis-client-resttemplate</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache-extras.beanshell</groupId>
<artifactId>bsh</artifactId>
<version>2.0b6</version>
</dependency>
</dependencies>
<build>
......
/*
* Copyright 2017 Global Crop Diversity Trust
*
* 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.
*/
package org.genesys2.server.servlet.controller.admin;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.io.Reader;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import org.genesys.glis.v1.api.GenesysApi;
import org.genesys2.server.service.GenesysFilterService;
import org.genesys2.server.service.GenesysService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import bsh.ConsoleInterface;
import bsh.EvalError;
import bsh.Interpreter;
/**
* Beanshell
*
* @author Matija Obreza
*/
@Controller
@RequestMapping("/admin/bsh")
@PreAuthorize("hasRole('ADMINISTRATOR')")
public class BeanshellController implements InitializingBean {
public static final Logger LOG = LoggerFactory.getLogger(BeanshellController.class);
@Autowired
private GenesysService genesysService;
@Autowired
private GenesysFilterService filterService;
@Autowired
private GenesysApi glisGenesys;
@PersistenceContext
private EntityManager entityManager;
@Autowired
private JdbcTemplate jdbcTemplate;
private ObjectMapper mapper;
private Map<String, Object> exposedBeans;
@Override
public void afterPropertiesSet() throws Exception {
mapper = new ObjectMapper();
mapper.disable(SerializationFeature.EAGER_SERIALIZER_FETCH);
mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
this.exposedBeans = new HashMap<>();
this.exposedBeans.put("json", mapper);
this.exposedBeans.put("genesysService", genesysService);
this.exposedBeans.put("filterService", filterService);
this.exposedBeans.put("glisGenesys", glisGenesys);
this.exposedBeans.put("entityManager", entityManager);
this.exposedBeans.put("jdbc", jdbcTemplate);
}
/**
* Render the form.
*/
@RequestMapping(method = RequestMethod.GET)
public ModelAndView defaultView(ModelMap model) {
model.addAttribute("beans", this.exposedBeans);
return new ModelAndView("/admin/beanshell", model);
}
/**
* Execute script in read-only transaction.
*
* @param model
* @param script
* @return
*/
@Transactional(readOnly = true, noRollbackFor = Exception.class)
@RequestMapping(method = RequestMethod.POST, params = { "readonly", "script" })
public String bshExecReadonly(RedirectAttributes redirectAttributes, @RequestParam(name = "script") String script) {
LOG.debug("Executing script in readonly mode\n{}", script);
return executeScript(redirectAttributes, script);
}
private String executeScript(RedirectAttributes redirectAttributes, String script) {
redirectAttributes.addFlashAttribute("script", script);
try {
ConsoleInterface console = new MemoryConsole();
Interpreter i = initializeInterpreter(console);
Object result = i.eval(script);
redirectAttributes.addFlashAttribute("result", result);
try {
redirectAttributes.addFlashAttribute("resultJson", mapper.writerWithDefaultPrettyPrinter().writeValueAsString(result));
} catch (Throwable e) {
redirectAttributes.addFlashAttribute("resultJson", "Could not convert to JSON: " + e.getMessage());
}
redirectAttributes.addFlashAttribute("console", console.toString());
} catch (final EvalError e) {
LOG.error(e.getMessage());
redirectAttributes.addFlashAttribute("error", new BeanshellError(e));
} catch (final Throwable e) {
LOG.error(e.getMessage(), e);
redirectAttributes.addFlashAttribute("exception", new BeanshellError(e));
}
return "redirect:/admin/bsh";
}
protected Interpreter initializeInterpreter(ConsoleInterface console) throws EvalError {
final Interpreter i = new Interpreter(console);
// beans
exposedBeans.entrySet().stream().forEach(entry -> {
try {
i.set(entry.getKey(), entry.getValue());
} catch (EvalError e) {
LOG.error("Trouble initializing bsh interpreter with key={}: {}", entry.getKey(), e.getMessage(), e);
}
});
// imports
i.eval("import org.genesys2.server.model.genesys.*;");
return i;
}
/**
* Byte array backed Beanshell console
*/
public static class MemoryConsole implements ConsoleInterface {
private final ByteArrayOutputStream baos = new ByteArrayOutputStream(1024 * 1024);
private final PrintStream stdout = new PrintStream(baos, true);
private final PrintStream stderr = new PrintStream(baos, true);
@Override
public Reader getIn() {
return null;
}
@Override
public PrintStream getOut() {
return stdout;
}
@Override
public PrintStream getErr() {
return stderr;
}
@Override
public void println(Object o) {
stdout.println(o);
}
@Override
public void print(Object o) {
stdout.print(o);
}
@Override
public void error(Object o) {
stderr.println(o);
}
@Override
public String toString() {
return baos.toString();
}
}
/**
* Serializable exception wrapper for flash attributes
*/
public static class BeanshellError implements Serializable {
private static final long serialVersionUID = 6436834633021529446L;
private String message;
private StackTraceElement[] stackTrace;
private int errorLineNumber;
private String errorText;
private String cause;
private String scriptStackTrace;
private String errorClass;
public BeanshellError(Throwable e) {
this.errorClass = e.getClass().getName();
this.message = e.getMessage();
this.stackTrace = e.getStackTrace();
if (e instanceof EvalError) {
EvalError ee = (EvalError) e;
this.errorLineNumber = ee.getErrorLineNumber();
this.errorText = ee.getErrorText();
this.scriptStackTrace = ee.getScriptStackTrace();
if (ee.getCause() != null)
this.cause = ee.getCause().getMessage();
}
}
public String getMessage() {
return message;
}
public StackTraceElement[] getStackTrace() {
return stackTrace;
}
public String getCause() {
return cause;
}
public int getErrorLineNumber() {
return errorLineNumber;
}
public String getErrorText() {
return errorText;
}
public String getScriptStackTrace() {
return scriptStackTrace;
}
public String getErrorClass() {
return errorClass;
}
}
}
......@@ -12,7 +12,7 @@
* 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.genesys2.server.servlet.filter;
......@@ -34,6 +34,9 @@ import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Handles the very important locale selection by URL
*/
public class LocaleURLFilter implements Filter {
private static final Logger LOG = LoggerFactory.getLogger(LocaleURLFilter.class);
......@@ -75,14 +78,11 @@ public class LocaleURLFilter implements Filter {
@Override
public void destroy() {
if (LOG.isDebugEnabled()) {
LOG.debug("Destroying LocaleURLFilter");
}
LOG.info("Destroying LocaleURLFilter");
}
@Override
public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain)
throws IOException, ServletException {
public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException {
final HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
final HttpServletResponse httpResponse = (HttpServletResponse) servletResponse;
final String url = httpRequest.getRequestURI().substring(httpRequest.getContextPath().length());
......@@ -92,9 +92,7 @@ public class LocaleURLFilter implements Filter {
return;
}
if (LOG.isDebugEnabled()) {
LOG.debug("Incoming URL: {}", url);
}
LOG.trace("Incoming URL: {}", url);
final Matcher matcher = localeUrlMatcher.matcher(url);
if (matcher.matches()) {
......@@ -132,33 +130,29 @@ public class LocaleURLFilter implements Filter {
httpRequest.setAttribute(REQUEST_LOCALE_ATTR, urlLocale);
httpRequest.setAttribute(REQUEST_INTERNAL_URL, getInternalUrl(remainingUrl, httpRequest.getQueryString()));
if (LOG.isDebugEnabled()) {
LOG.debug("URL matches! lang={} remaining={}", urlLanguage, remainingUrl);
LOG.debug("Country: {} Lang: {} locale={}", urlLocale.getCountry(), urlLocale.getLanguage(), urlLocale);
if (LOG.isTraceEnabled()) {
LOG.trace("URL matches! lang={} remaining={}", urlLanguage, remainingUrl);
LOG.trace("Country: {} Lang: {} locale={}", urlLocale.getCountry(), urlLocale.getLanguage(), urlLocale);
final Enumeration<String> attrNames = httpRequest.getAttributeNames();
while (attrNames.hasMoreElements()) {
final String attrName = attrNames.nextElement();
LOG.debug("Request attr {} = {}", attrName, httpRequest.getAttribute(attrName));
LOG.trace("Request attr {} = {}", attrName, httpRequest.getAttribute(attrName));
}
LOG.debug("Proxying request to remaining URL {}", remainingUrl);
LOG.trace("Proxying request to remaining URL {}", remainingUrl);
}
final LocaleWrappedServletResponse localeResponse = new LocaleWrappedServletResponse(httpResponse, localeUrlMatcher, urlLanguage,
defaultLocale.toLanguageTag());
final LocaleWrappedServletResponse localeResponse = new LocaleWrappedServletResponse(httpResponse, localeUrlMatcher, urlLanguage, defaultLocale.toLanguageTag());
final LocaleWrappedServletRequest localeRequest = new LocaleWrappedServletRequest(httpRequest, url, remainingUrl);
// request.getRequestDispatcher(remainingUrl == null ? "/" :
// remainingUrl).forward(servletRequest, localeResponse);
filterChain.doFilter(localeRequest, localeResponse);
} else {
if (LOG.isDebugEnabled()) {
LOG.debug("No match on url {}", url);
}
LOG.trace("No match on url {}", url);
httpRequest.setAttribute(REQUEST_INTERNAL_URL, getInternalUrl(url, httpRequest.getQueryString()));
final LocaleWrappedServletResponse localeResponse = new LocaleWrappedServletResponse(httpResponse, localeUrlMatcher, null,
defaultLocale.toLanguageTag());
final LocaleWrappedServletResponse localeResponse = new LocaleWrappedServletResponse(httpResponse, localeUrlMatcher, null, defaultLocale.toLanguageTag());
filterChain.doFilter(servletRequest, localeResponse);
}
}
......
......@@ -51,9 +51,7 @@ public class LocaleWrappedServletResponse extends HttpServletResponseWrapper {
return super.encodeURL(url);
} else {
String encodedURL = prefix + super.encodeURL(url);
if (LOG.isDebugEnabled()) {
LOG.debug("encodeURL {} to {}", url, encodedURL);
}
LOG.trace("encodeURL {} to {}", url, encodedURL);
return encodedURL;
}
}
......@@ -65,9 +63,7 @@ public class LocaleWrappedServletResponse extends HttpServletResponseWrapper {
return super.encodeUrl(url);
} else {
String encodedURL = prefix + super.encodeUrl(url);
if (LOG.isDebugEnabled()) {
LOG.debug("encodeUrl {} to {}", url, encodedURL);
}
LOG.trace("encodeUrl {} to {}", url, encodedURL);
return encodedURL;
}
}
......@@ -78,9 +74,7 @@ public class LocaleWrappedServletResponse extends HttpServletResponseWrapper {
return super.encodeRedirectURL(url);
} else {
String encodedURL = prefix + super.encodeRedirectURL(url);
if (LOG.isDebugEnabled()) {
LOG.debug("encodeRedirectURL {} to {}", url, encodedURL);
}
LOG.trace("encodeRedirectURL {} to {}", url, encodedURL);
return encodedURL;
}
}
......@@ -92,9 +86,7 @@ public class LocaleWrappedServletResponse extends HttpServletResponseWrapper {
return super.encodeRedirectUrl(url);
} else {
String encodedURL = prefix + super.encodeRedirectUrl(url);
if (LOG.isDebugEnabled()) {
LOG.debug("encodeRedirectUrl {} to {}", url, encodedURL);
}
LOG.trace("encodeRedirectUrl {} to {}", url, encodedURL);
return encodedURL;
}
}
......
......@@ -749,6 +749,7 @@ menu.admin.repository=Repository
menu.admin.repository.files=Repository file manager
menu.admin.repository.galleries=Image galleries
menu.admin.hazelcast=Hazelcast
menu.admin.beanshell=Beanshell
news.content.page.all.title=News list
news.archive.title=News Archive
......
......@@ -3,171 +3,213 @@
<!DOCTYPE html>
<html lang="${pageContext.response.locale.language}" dir="${pageContext.response.locale.language=='fa' || pageContext.response.locale.language=='ar' ? 'rtl' : 'ltr'}" class="genesys-page">
<head>
<title><sitemesh:write property="title" /></title>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="language" content="${pageContext.response.locale.language}" />
<meta name="robots" content="noindex, nofollow" />
<!-- CSRF protection-->
<meta name="_csrf" content="${_csrf.token}" />
<!-- default header name is X-CSRF-TOKEN -->
<meta name="_csrf_header" content="${_csrf.headerName}" />
<!-- Links -->
<link rel="shortcut icon" href="<c:url value="${cdnServers.next}/html/1/images/genesys.png" />" />
<!-- opensearch.org -->
<link rel="search" hreflang="${pageContext.response.locale.language}" type="application/opensearchdescription+xml" href="<c:url value="/acn/opensearch/desc" />"
title="<spring:message code="search.input.placeholder" />" />
<!-- l10n -->
<link rel="alternate" hreflang="en" href="<c:url value="/en${pageContext.request.getAttribute('org.genesys2.server.servlet.filter.LocaleURLFilter.INTERNALURL')}" />" />
<link rel="alternate" hreflang="ar" href="<c:url value="/ar${pageContext.request.getAttribute('org.genesys2.server.servlet.filter.LocaleURLFilter.INTERNALURL')}" />" />
<link rel="alternate" hreflang="de" href="<c:url value="/de${pageContext.request.getAttribute('org.genesys2.server.servlet.filter.LocaleURLFilter.INTERNALURL')}" />" />
<link rel="alternate" hreflang="es" href="<c:url value="/es${pageContext.request.getAttribute('org.genesys2.server.servlet.filter.LocaleURLFilter.INTERNALURL')}" />" />
<link rel="alternate" hreflang="fa" href="<c:url value="/fa${pageContext.request.getAttribute('org.genesys2.server.servlet.filter.LocaleURLFilter.INTERNALURL')}" />" />
<link rel="alternate" hreflang="fr" href="<c:url value="/fr${pageContext.request.getAttribute('org.genesys2.server.servlet.filter.LocaleURLFilter.INTERNALURL')}" />" />
<link rel="alternate" hreflang="pt" href="<c:url value="/pt${pageContext.request.getAttribute('org.genesys2.server.servlet.filter.LocaleURLFilter.INTERNALURL')}" />" />
<link rel="alternate" hreflang="ru" href="<c:url value="/ru${pageContext.request.getAttribute('org.genesys2.server.servlet.filter.LocaleURLFilter.INTERNALURL')}" />" />
<link rel="alternate" hreflang="zh" href="<c:url value="/zh${pageContext.request.getAttribute('org.genesys2.server.servlet.filter.LocaleURLFilter.INTERNALURL')}" />" />
<!-- Custom styles for this template -->
<%@ include file="css.jsp" %>
<!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!--[if lt IE 9]>
<head>
<title><sitemesh:write property="title"/></title>
<meta charset="utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<meta name="language" content="${pageContext.response.locale.language}"/>
<meta name="robots" content="noindex, nofollow"/>
<!-- CSRF protection-->
<meta name="_csrf" content="${_csrf.token}"/>
<!-- default header name is X-CSRF-TOKEN -->
<meta name="_csrf_header" content="${_csrf.headerName}"/>
<!-- Links -->
<link rel="shortcut icon" href="<c:url value='${cdnServers.next}/html/1/images/genesys.png' />" />
<!-- opensearch.org -->
<link rel="search" hreflang="${pageContext.response.locale.language}" type="application/opensearchdescription+xml" href="<c:url value='/acn/opensearch/desc' />" title="<spring:message code='search.input.placeholder' />"/>
<!-- l10n -->
<link rel="alternate" hreflang="en" href="<c:url value='/en${pageContext.request.getAttribute('org.genesys2.server.servlet.filter.LocaleURLFilter.INTERNALURL')}' />"/>
<link rel="alternate" hreflang="ar" href="<c:url value='/ar${pageContext.request.getAttribute('org.genesys2.server.servlet.filter.LocaleURLFilter.INTERNALURL')}' />"/>
<link rel="alternate" hreflang="de" href="<c:url value='/de${pageContext.request.getAttribute('org.genesys2.server.servlet.filter.LocaleURLFilter.INTERNALURL')}' />"/>
<link rel="alternate" hreflang="es" href="<c:url value='/es${pageContext.request.getAttribute('org.genesys2.server.servlet.filter.LocaleURLFilter.INTERNALURL')}' />"/>
<link rel="alternate" hreflang="fa" href="<c:url value='/fa${pageContext.request.getAttribute('org.genesys2.server.servlet.filter.LocaleURLFilter.INTERNALURL')}' />"/>
<link rel="alternate" hreflang="fr" href="<c:url value='/fr${pageContext.request.getAttribute('org.genesys2.server.servlet.filter.LocaleURLFilter.INTERNALURL')}' />"/>
<link rel="alternate" hreflang="pt" href="<c:url value='/pt${pageContext.request.getAttribute('org.genesys2.server.servlet.filter.LocaleURLFilter.INTERNALURL')}' />"/>
<link rel="alternate" hreflang="ru" href="<c:url value='/ru${pageContext.request.getAttribute('org.genesys2.server.servlet.filter.LocaleURLFilter.INTERNALURL')}' />"/>
<link rel="alternate" hreflang="zh" href="<c:url value='/zh${pageContext.request.getAttribute('org.genesys2.server.servlet.filter.LocaleURLFilter.INTERNALURL')}' />"/>
<!-- Custom styles for this template -->
<%@ include file="css.jsp" %>
<!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
<script src="https://oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
<![endif]-->
<sitemesh:write property="head" />
<meta name="author" content="Global Crop Diversity Trust" />
</head>
<security:authentication var="user" property="principal" />
<![endif]-->
<sitemesh:write property="head"/>
<meta name="author" content="Global Crop Diversity Trust"/>
</head>
<security:authentication var="user" property="principal"/>
<body>
<%@ include file="header.jsp" %>
<div class="admin-page">
<nav class="navbar navbar-default navbar-inverse navbar-fixed-top">
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class="humburger-btn navbar-toggle collapsed pull-left" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<%--<a class="navbar-brand" href="<c:url value='/admin/' />"> <spring:message code="page.home.title" /> </a>--%>
<a class="navbar-brand nav-logo clearfix" href="<c:url value='/admin/' />"><img src="<c:url value='${cdnServers.next}/html/1/images/GENESYS-ICON.svg' />"/><img src="/html/1/images/GENESYS-LOGO.svg"/></a>
</div>
<div id="navbar" class="collapse navbar-collapse">
<div class="col-md-7 col-lg-10 no-space">
<div class="navbar-header">
<button type="button" class="humburger-btn navbar-toggle collapsed pull-left" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<%--<a class="navbar-brand" href="<c:url value='/admin/' />"> <spring:message code="page.home.title" /> </a>--%>
<a class="navbar-brand nav-logo clearfix" href="<c:url value='/admin/' />"><img src="<c:url value='${cdnServers.next}/html/1/images/GENESYS-ICON.svg' />"/><img src="<c:url value='${cdnServers.next}/html/1/images/GENESYS-LOGO.svg' />"/></a>
</div>
<ul class="nav navbar-nav nav-menu-admin">
<li class="hidden-sm">
<a href="<c:url value='/welcome' />" title="<spring:message code='menu.home' />">
<spring:message code="menu.home"/>
</a>
</li>
<li class="dropdown" id="user-management-menu-item">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
Users
<span class="caret"></span>
</a>
<ul class="dropdown-menu pull-left">
<li>
<a href="<c:url value='/admin/users/' />">
<spring:message code="user.pulldown.users"/></a>
</li>
<li>
<a href="<c:url value='/admin/teams/' />">
<spring:message code="user.pulldown.teams"/></a>
</li>
<li>
<a href="<c:url value='/admin/oauth-clients/' />" class="">OAuth clients</a>
</li>
</ul>
</li>
<li class="dropdown" id="repository-menu-item">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
<spring:message code="menu.admin.repository"/>
<span class="caret"></span>
</a>
<ul class="dropdown-menu pull-left">
<li>
<a href="<c:url value='/admin/r/files/' />">
<spring:message code="menu.admin.repository.files"/>
</a>
</li>
<li>
<a href="<c:url value='/admin/r/g' />">
<spring:message code="menu.admin.repository.galleries"/>
</a>
</li>