diff --git a/server/CHANGES.txt b/server/CHANGES.txt
index 2fc3b3bfe..c21c78c8f 100644
--- a/server/CHANGES.txt
+++ b/server/CHANGES.txt
@@ -1,3 +1,7 @@
+1.8
+--------------------
+o Consoles in webadmin can now be disabled.
+
1.8.M06 (2012-07-06)
--------------------
o Fixed issue that stopped the server from starting without the UDC-jars.
diff --git a/server/src/docs/dev/server-configuration.txt b/server/src/docs/dev/server-configuration.txt
index 27f9d7080..5005889af 100644
--- a/server/src/docs/dev/server-configuration.txt
+++ b/server/src/docs/dev/server-configuration.txt
@@ -167,3 +167,22 @@ wrapper.java.additional.3=-Xloggc:data/log/neo4j-gc.log
This line is already present and needs uncommenting. Note also that logging is not directed to console ;
You will find the logging statements in 'data/log/ne4j-gc.log' or whatever directory you set at the option.
+
+=== Disabling console types in Webadmin ===
+
+You may, for security reasons, want to disable the Gremlin console and/or the Neo4j Shell in Webadmin.
+Both of them allow arbitrary code execution, and so they could constitute a security risk if you do not trust all users of your Neo4j Server.
+
+In the 'conf/neo4j-server.properties' file:
+
+[source]
+----
+# To disable both Neo4j Shell and Gremlin:
+org.neo4j.server.manage.console_engines=
+
+# To enable only the Neo4j Shell:
+org.neo4j.server.manage.console_engines=shell
+
+# To enable both
+org.neo4j.server.manage.console_engines=gremlin,shell
+----
diff --git a/server/src/functionaltest/java/org/neo4j/server/webadmin/rest/ConfigureEnabledManagementConsolesTest.java b/server/src/functionaltest/java/org/neo4j/server/webadmin/rest/ConfigureEnabledManagementConsolesTest.java
new file mode 100644
index 000000000..0fad4428a
--- /dev/null
+++ b/server/src/functionaltest/java/org/neo4j/server/webadmin/rest/ConfigureEnabledManagementConsolesTest.java
@@ -0,0 +1,80 @@
+/**
+ * Copyright (c) 2002-2012 "Neo Technology,"
+ * Network Engine for Objects in Lund AB [http://neotechnology.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.server.webadmin.rest;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+import static org.neo4j.server.helpers.ServerBuilder.server;
+
+import org.junit.After;
+import org.junit.Test;
+import org.neo4j.server.NeoServer;
+import org.neo4j.server.configuration.Configurator;
+import org.neo4j.server.rest.JaxRsResponse;
+import org.neo4j.server.rest.RestRequest;
+import org.neo4j.test.server.ExclusiveServerTestBase;
+
+public class ConfigureEnabledManagementConsolesTest extends ExclusiveServerTestBase {
+
+ private NeoServer server;
+
+ @After
+ public void stopTheServer()
+ {
+ server.stop();
+ }
+
+ @Test
+ public void shouldBeAbleToDisableGremlinConsole() throws Exception {
+ server = server().withProperty(Configurator.MANAGEMENT_CONSOLE_ENGINES, "shell").build();
+ server.start();
+
+ assertThat(exec("g","gremlin").getStatus(), is(400));
+ assertThat(exec("ls","shell").getStatus(), is(200));
+ }
+
+ @Test
+ public void shouldBeAbleToExplicitlySetConsolesToEnabled() throws Exception
+ {
+ server = server().withProperty(Configurator.MANAGEMENT_CONSOLE_ENGINES, "shell,gremlin").build();
+ server.start();
+
+ assertThat(exec("g","gremlin").getStatus(), is(200));
+ assertThat(exec("ls","shell").getStatus(), is(200));
+ }
+
+
+
+ @Test
+ public void gremlinAndShellConsolesShouldBeEnabledByDefault() throws Exception {
+ server = server().build();
+ server.start();
+
+ assertThat(exec("g","gremlin").getStatus(), is(200));
+ assertThat(exec("ls","shell").getStatus(), is(200));
+ }
+
+ private JaxRsResponse exec(String command, String engine)
+ {
+ return RestRequest.req().post(server.baseUri() + "db/manage/server/console", "{" +
+ "\"engine\":\""+engine+"\"," +
+ "\"command\":\""+command+"\\n\"}");
+ }
+}
\ No newline at end of file
diff --git a/server/src/functionaltest/java/org/neo4j/server/webadmin/rest/ConsoleServiceTest.java b/server/src/functionaltest/java/org/neo4j/server/webadmin/rest/ConsoleServiceTest.java
new file mode 100644
index 000000000..e26f1d96c
--- /dev/null
+++ b/server/src/functionaltest/java/org/neo4j/server/webadmin/rest/ConsoleServiceTest.java
@@ -0,0 +1,111 @@
+/**
+ * Copyright (c) 2002-2012 "Neo Technology,"
+ * Network Engine for Objects in Lund AB [http://neotechnology.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.server.webadmin.rest;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsString;
+import static org.junit.Assert.assertEquals;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+
+import javax.ws.rs.core.Response;
+
+import org.junit.Test;
+import org.neo4j.server.database.Database;
+import org.neo4j.server.rest.repr.OutputFormat;
+import org.neo4j.server.rest.repr.formats.JsonFormat;
+import org.neo4j.server.webadmin.console.ScriptSession;
+
+public class ConsoleServiceTest
+{
+ private final URI uri = URI.create( "http://peteriscool.com:6666/" );
+
+ @Test
+ public void correctRepresentation() throws URISyntaxException, UnsupportedEncodingException
+ {
+ ConsoleService consoleService = new ConsoleService( new ShellOnlyConsoleSessionFactory(), null, new OutputFormat( new JsonFormat(), uri, null ) );
+
+ Response consoleResponse = consoleService.getServiceDefinition();
+
+ assertEquals( 200, consoleResponse.getStatus() );
+ String response = decode( consoleResponse );
+ assertThat( response, containsString( "resources" ) );
+ assertThat( response, containsString( uri.toString() ) );
+ }
+
+ @Test
+ public void advertisesAvailableConsoleEngines() throws URISyntaxException, UnsupportedEncodingException
+ {
+ ConsoleService consoleServiceWithJustShellEngine = new ConsoleService( new ShellOnlyConsoleSessionFactory(), null, new OutputFormat( new JsonFormat(), uri, null ) );
+
+ String response = decode( consoleServiceWithJustShellEngine.getServiceDefinition());
+
+ assertThat( response, containsString( "\"engines\" : [ \"shell\" ]" ) );
+
+ ConsoleService consoleServiceWithShellAndGremlin = new ConsoleService( new GremlinAndShellConsoleSessionFactory(), null, new OutputFormat( new JsonFormat(), uri, null ) );
+
+ response = decode( consoleServiceWithShellAndGremlin.getServiceDefinition());
+
+ assertThat( response, containsString( "\"engines\" : [ \"shell\", \"gremlin\" ]" ) );
+ }
+
+ private String decode( final Response response ) throws UnsupportedEncodingException
+ {
+ return new String( (byte[]) response.getEntity(), "UTF-8" );
+ }
+
+ private static class ShellOnlyConsoleSessionFactory implements ConsoleSessionFactory
+ {
+ @Override
+ public ScriptSession createSession(String engineName, Database database)
+ {
+ return null;
+ }
+
+ @Override
+ public Iterable supportedEngines()
+ {
+ return new ArrayList(){{
+ add("shell");
+ }};
+ }
+ }
+
+ private static class GremlinAndShellConsoleSessionFactory implements ConsoleSessionFactory
+ {
+ @Override
+ public ScriptSession createSession(String engineName, Database database)
+ {
+ return null;
+ }
+
+ @Override
+ public Iterable supportedEngines()
+ {
+ return new ArrayList(){{
+ add("shell");
+ add("gremlin");
+ }};
+ }
+ }
+}
diff --git a/server/src/functionaltest/java/org/neo4j/server/webadmin/rest/GremlinConsoleServiceTest.java b/server/src/functionaltest/java/org/neo4j/server/webadmin/rest/GremlinConsoleServiceTest.java
index ec7e7df68..664a0cdc9 100644
--- a/server/src/functionaltest/java/org/neo4j/server/webadmin/rest/GremlinConsoleServiceTest.java
+++ b/server/src/functionaltest/java/org/neo4j/server/webadmin/rest/GremlinConsoleServiceTest.java
@@ -25,7 +25,7 @@
import java.io.UnsupportedEncodingException;
import java.net.URI;
-import java.net.URISyntaxException;
+import java.util.ArrayList;
import javax.ws.rs.core.Response;
@@ -40,7 +40,7 @@
import org.neo4j.server.webadmin.console.ScriptSession;
import org.neo4j.test.ImpermanentGraphDatabase;
-public class GremlinConsoleServiceTest implements SessionFactory
+public class GremlinConsoleServiceTest implements ConsoleSessionFactory
{
private ConsoleService consoleService;
private Database database;
@@ -76,17 +76,6 @@ public void canCreateNodesInGremlinLand() throws UnsupportedEncodingException
assertEquals( 200, evaluatedGremlinResponse.getStatus() );
assertThat( response, containsString( "v[2]" ) );
}
-
- @Test
- public void correctRepresentation() throws URISyntaxException, UnsupportedEncodingException
- {
- Response consoleResponse = consoleService.getServiceDefinition();
-
- assertEquals( 200, consoleResponse.getStatus() );
- String response = decode( consoleResponse );
- assertThat( response, containsString( "resources" ) );
- assertThat( response, containsString( uri.toString() ) );
- }
@Before
public void setUp() throws Exception
@@ -106,4 +95,12 @@ public ScriptSession createSession( String engineName, Database database )
{
return new GremlinSession( database );
}
+
+ @Override
+ public Iterable supportedEngines()
+ {
+ return new ArrayList(){{
+ add("gremlin");
+ }};
+ }
}
diff --git a/server/src/functionaltest/java/org/neo4j/server/webadmin/rest/Neo4jShellConsoleSessionTest.java b/server/src/functionaltest/java/org/neo4j/server/webadmin/rest/Neo4jShellConsoleSessionTest.java
index bc14d21e8..a7ce436af 100644
--- a/server/src/functionaltest/java/org/neo4j/server/webadmin/rest/Neo4jShellConsoleSessionTest.java
+++ b/server/src/functionaltest/java/org/neo4j/server/webadmin/rest/Neo4jShellConsoleSessionTest.java
@@ -25,6 +25,7 @@
import java.io.UnsupportedEncodingException;
import java.net.URI;
+import java.util.ArrayList;
import java.util.List;
import javax.ws.rs.core.Response;
@@ -44,7 +45,7 @@
import org.neo4j.shell.ShellSettings;
import org.neo4j.test.TestGraphDatabaseFactory;
-public class Neo4jShellConsoleSessionTest implements SessionFactory
+public class Neo4jShellConsoleSessionTest implements ConsoleSessionFactory
{
private static final String LN = System.getProperty("line.separator");
private ConsoleService consoleService;
@@ -97,4 +98,12 @@ private List decode( final Response response ) throws UnsupportedEncodin
{
return (List)JsonHelper.readJson(new String( (byte[]) response.getEntity(), "UTF-8" ));
}
+
+ @Override
+ public Iterable supportedEngines()
+ {
+ return new ArrayList(){{
+ add("shell");
+ }};
+ }
}
diff --git a/server/src/main/coffeescript/neo4j/webadmin/modules/console/ConsoleRouter.coffee b/server/src/main/coffeescript/neo4j/webadmin/modules/console/ConsoleRouter.coffee
index 259f85495..d8bc660fb 100644
--- a/server/src/main/coffeescript/neo4j/webadmin/modules/console/ConsoleRouter.coffee
+++ b/server/src/main/coffeescript/neo4j/webadmin/modules/console/ConsoleRouter.coffee
@@ -30,10 +30,10 @@ define(
class ConsoleRouter extends Router
routes :
- "/console/" : "console"
- "/console/:type" : "console"
+ "/console/" : "showConsole"
+ "/console/:type" : "showConsole"
- consoleType : "shell"
+ consoleType : "http"
init : (appState) =>
@appState = appState
@@ -47,29 +47,59 @@ define(
@shellState = new Console(server:@appState.get("server"), lang:"shell")
@httpState = new HttpConsole(server:@appState.get("server"), lang:"http")
- @views =
- gremlin : new GremlinConsoleView
- appState : @appState
- consoleState : @gremlinState
- lang: "gremlin"
- shell : new ShellConsoleView
- appState : @appState
- consoleState : @shellState
- lang: "shell"
- http : new HttpConsoleView
- appState : @appState
- consoleState : @httpState
- lang: "http"
+ # Ask the server what console engines are available
+ self = this
+ @appState.getServer().manage.console.availableEngines (engines) ->
+ self.onAvailableEnginesLoaded(engines)
- console : (type=false) =>
+ showConsole : (type=false) =>
@saveLocation()
+
if type is false then type = @consoleType
@consoleType = type
- @appState.set( mainView : @getConsoleView(type) )
- @getConsoleView(type).focusOnInputField()
- getConsoleView : (type) =>
- @views[type]
+ if @views?
+ if @views[type]?
+ view = @views[type]
+ else
+ alert "Unsupported console type: '#{type}', is it disabled in the server?."
+ view = @views['http']
+
+ @appState.set( mainView : view )
+ view.focusOnInputField()
+ else
+ # Set a flag to remember to re-run this method when
+ # available engines has been loaded.
+ @renderWhenEnginesAreLoaded = true
+
+ onAvailableEnginesLoaded : (engines) ->
+ engines.push('http') # HTTP is always available
+ @views =
+ http : new HttpConsoleView
+ appState : @appState
+ consoleState : @httpState
+ lang : "http"
+ engines : engines
+
+ if _(engines).indexOf('gremlin') > -1
+ @views.gremlin = new GremlinConsoleView
+ appState : @appState
+ consoleState : @gremlinState
+ lang : "gremlin"
+ engines : engines
+
+ if _(engines).indexOf('shell') > -1
+ @views.shell = new ShellConsoleView
+ appState : @appState
+ consoleState : @shellState
+ lang : "shell"
+ engines : engines
+
+ # Use shell per default if it is available
+ @consoleType = "shell"
+
+ if @renderWhenEnginesAreLoaded?
+ @showConsole()
#
# Bootstrapper SPI
diff --git a/server/src/main/coffeescript/neo4j/webadmin/modules/console/views/ConsoleView.coffee b/server/src/main/coffeescript/neo4j/webadmin/modules/console/views/ConsoleView.coffee
index 3108c25ef..39cd3c9b3 100644
--- a/server/src/main/coffeescript/neo4j/webadmin/modules/console/views/ConsoleView.coffee
+++ b/server/src/main/coffeescript/neo4j/webadmin/modules/console/views/ConsoleView.coffee
@@ -36,6 +36,7 @@ define(
@appState = opts.appState
@consoleState = opts.consoleState
@lang = opts.lang
+ @availableEngines = opts.engines
@consoleState.bind("change", @renderConsole)
consoleKeyUp : (ev) =>
@@ -63,7 +64,8 @@ define(
$("#console-input").putCursorAtEnd()
renderConsole : ()=>
- $("#console-base",@el).html consoleTemplate(
+ $("#console-base",@el).html consoleTemplate(
+ engines : @availableEngines
lines : @consoleState.get "lines"
prompt : @consoleState.get "prompt"
showPrompt : @consoleState.get "showPrompt"
diff --git a/server/src/main/coffeescript/neo4j/webadmin/modules/console/views/ShellConsoleView.coffee b/server/src/main/coffeescript/neo4j/webadmin/modules/console/views/ShellConsoleView.coffee
index 63edc116c..644176709 100644
--- a/server/src/main/coffeescript/neo4j/webadmin/modules/console/views/ShellConsoleView.coffee
+++ b/server/src/main/coffeescript/neo4j/webadmin/modules/console/views/ShellConsoleView.coffee
@@ -29,7 +29,9 @@ define(
class ShellConsoleView extends ConsoleView
render : =>
- $(@el).html(baseTemplate(current:'shell'))
+ $(@el).html(baseTemplate(
+ current:'shell'
+ ))
@renderConsole()
return this
)
diff --git a/server/src/main/coffeescript/neo4j/webadmin/modules/console/views/console.haml b/server/src/main/coffeescript/neo4j/webadmin/modules/console/views/console.haml
index 87f21e9e6..e174e21ec 100644
--- a/server/src/main/coffeescript/neo4j/webadmin/modules/console/views/console.haml
+++ b/server/src/main/coffeescript/neo4j/webadmin/modules/console/views/console.haml
@@ -1,11 +1,16 @@
-#console-tabs
- %ul.button-bar.grouped
- %li
- %a(href="#/console/shell" class="button #{(current == 'shell' ? 'current':'')}") Neo4j Shell
- %li
- %a(href="#/console/gremlin" class="button #{(current == 'gremlin' ? 'current':'')}") Gremlin
- %li
- %a(href="#/console/http" class="button #{(current == 'http' ? 'current':'')}") HTTP
+:if engines.length > 1
+ #console-tabs
+ %ul.button-bar.grouped
+ :if _(engines).indexOf('shell') > -1
+ %li
+ %a(href="#/console/shell" class="button #{(current == 'shell' ? 'current':'')}") Neo4j Shell
+ :if _(engines).indexOf('gremlin') > -1
+ %li
+ %a(href="#/console/gremlin" class="button #{(current == 'gremlin' ? 'current':'')}") Gremlin
+ :if _(engines).indexOf('http') > -1
+ %li
+ %a(href="#/console/http" class="button #{(current == 'http' ? 'current':'')}") HTTP
+
#console.pad
%ul
:each line in lines
diff --git a/server/src/main/java/org/neo4j/server/configuration/Configurator.java b/server/src/main/java/org/neo4j/server/configuration/Configurator.java
index e8dda666b..75c94a686 100644
--- a/server/src/main/java/org/neo4j/server/configuration/Configurator.java
+++ b/server/src/main/java/org/neo4j/server/configuration/Configurator.java
@@ -20,7 +20,9 @@
package org.neo4j.server.configuration;
import java.io.File;
+import java.util.ArrayList;
import java.util.Iterator;
+import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -29,6 +31,7 @@
import org.neo4j.kernel.impl.util.StringLogger;
import org.neo4j.kernel.info.DiagnosticsExtractor;
import org.neo4j.kernel.info.DiagnosticsPhase;
+import org.neo4j.server.webadmin.rest.SessionFactoryImpl;
public interface Configurator
{
@@ -62,6 +65,14 @@ public interface Configurator
String DEFAULT_WEB_ADMIN_PATH = "/webadmin";
String RRDB_LOCATION_PROPERTY_KEY = "org.neo4j.server.webadmin.rrdb.location";
+
+ String MANAGEMENT_CONSOLE_ENGINES = "org.neo4j.server.manage.console_engines";
+ List DEFAULT_MANAGEMENT_CONSOLE_ENGINES = new ArrayList(){
+ private static final long serialVersionUID = 6621747998288594121L;
+ {
+ add(SessionFactoryImpl.ConsoleEngineCreator.SHELL.name().toLowerCase());
+ add(SessionFactoryImpl.ConsoleEngineCreator.GREMLIN.name().toLowerCase());
+ }};
String THIRD_PARTY_PACKAGES_KEY = "org.neo4j.server.thirdparty_jaxrs_classes";
diff --git a/server/src/main/java/org/neo4j/server/webadmin/console/GremlinSession.java b/server/src/main/java/org/neo4j/server/webadmin/console/GremlinSession.java
index 23f73677b..2a830db03 100644
--- a/server/src/main/java/org/neo4j/server/webadmin/console/GremlinSession.java
+++ b/server/src/main/java/org/neo4j/server/webadmin/console/GremlinSession.java
@@ -34,6 +34,7 @@
import org.neo4j.helpers.Pair;
import org.neo4j.server.database.Database;
import org.neo4j.server.database.DatabaseBlockedException;
+import org.neo4j.server.logging.Logger;
import com.tinkerpop.blueprints.pgm.TransactionalGraph;
import com.tinkerpop.blueprints.pgm.impls.neo4j.Neo4jGraph;
@@ -42,6 +43,8 @@ public class GremlinSession implements ScriptSession
{
private static final String INIT_FUNCTION = "init()";
+ private static final Logger log = Logger.getLogger(GremlinSession.class);
+
protected GremlinWebConsole scriptEngine;
private final Database database;
private final IO io;
@@ -99,13 +102,17 @@ public Pair evaluate( String script )
}
else
{
- scriptEngine.execute( script );
- result = baos.toString();
- resetIO();
+ try {
+ scriptEngine.execute( script );
+ result = baos.toString();
+ } finally {
+ resetIO();
+ }
}
}
catch ( GroovyRuntimeException ex )
{
+ log.error(ex);
result = ex.getMessage();
}
return Pair.of( result, null );
diff --git a/server/src/main/java/org/neo4j/server/webadmin/rest/ConsoleService.java b/server/src/main/java/org/neo4j/server/webadmin/rest/ConsoleService.java
index f811ad5fd..a7eff88f3 100644
--- a/server/src/main/java/org/neo4j/server/webadmin/rest/ConsoleService.java
+++ b/server/src/main/java/org/neo4j/server/webadmin/rest/ConsoleService.java
@@ -33,7 +33,9 @@
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
+import org.apache.commons.configuration.Configuration;
import org.neo4j.helpers.Pair;
+import org.neo4j.server.configuration.Configurator;
import org.neo4j.server.database.Database;
import org.neo4j.server.logging.Logger;
import org.neo4j.server.rest.repr.BadInputException;
@@ -44,31 +46,33 @@
import org.neo4j.server.rest.repr.RepresentationType;
import org.neo4j.server.rest.repr.ValueRepresentation;
import org.neo4j.server.webadmin.console.ScriptSession;
-import org.neo4j.server.webadmin.rest.representations.ServiceDefinitionRepresentation;
+import org.neo4j.server.webadmin.rest.representations.ConsoleServiceRepresentation;
@Path( ConsoleService.SERVICE_PATH )
public class ConsoleService implements AdvertisableService
{
+ public static final String SERVICE_PATH = "server/console";
+
private static final String SERVICE_NAME = "console";
- static final String SERVICE_PATH = "server/console";
- private final SessionFactory sessionFactory;
+ private static final Logger log = Logger.getLogger( ConsoleService.class );
+
+ private final ConsoleSessionFactory sessionFactory;
private final Database database;
private final OutputFormat output;
- public ConsoleService( SessionFactory sessionFactory, Database database, OutputFormat output )
+ @SuppressWarnings("unchecked")
+ public ConsoleService( @Context Configuration config, @Context Database database, @Context HttpServletRequest req, @Context OutputFormat output )
{
- this.sessionFactory = sessionFactory;
- this.database = database;
- this.output = output;
+ this( new SessionFactoryImpl( req.getSession( true ), config.getList(Configurator.MANAGEMENT_CONSOLE_ENGINES, Configurator.DEFAULT_MANAGEMENT_CONSOLE_ENGINES) ), database, output );
}
- public ConsoleService( @Context Database database, @Context HttpServletRequest req, @Context OutputFormat output )
+ public ConsoleService( ConsoleSessionFactory sessionFactory, Database database, OutputFormat output )
{
- this( new SessionFactoryImpl( req.getSession( true ) ), database, output );
+ this.sessionFactory = sessionFactory;
+ this.database = database;
+ this.output = output;
}
- Logger log = Logger.getLogger( ConsoleService.class );
-
@Override
public String getName()
{
@@ -84,8 +88,7 @@ public String getServerPath()
@GET
public Response getServiceDefinition()
{
- ServiceDefinitionRepresentation result = new ServiceDefinitionRepresentation( SERVICE_PATH );
- result.resourceUri( "exec", "" );
+ ConsoleServiceRepresentation result = new ConsoleServiceRepresentation( SERVICE_PATH, sessionFactory.supportedEngines() );
return output.ok( result );
}
@@ -110,7 +113,13 @@ public Response exec( @Context InputFormat input, String data )
.build();
}
- ScriptSession scriptSession = getSession( args );
+ ScriptSession scriptSession;
+ try {
+ scriptSession = getSession( args );
+ } catch(IllegalArgumentException e) {
+ return output.badRequest(e);
+ }
+
log.trace( scriptSession.toString() );
try
{
diff --git a/server/src/main/java/org/neo4j/server/webadmin/rest/SessionFactory.java b/server/src/main/java/org/neo4j/server/webadmin/rest/ConsoleSessionFactory.java
similarity index 92%
rename from server/src/main/java/org/neo4j/server/webadmin/rest/SessionFactory.java
rename to server/src/main/java/org/neo4j/server/webadmin/rest/ConsoleSessionFactory.java
index caed3ddb3..217833e3d 100644
--- a/server/src/main/java/org/neo4j/server/webadmin/rest/SessionFactory.java
+++ b/server/src/main/java/org/neo4j/server/webadmin/rest/ConsoleSessionFactory.java
@@ -22,7 +22,8 @@
import org.neo4j.server.database.Database;
import org.neo4j.server.webadmin.console.ScriptSession;
-public interface SessionFactory
+public interface ConsoleSessionFactory
{
ScriptSession createSession( String engineName, Database database );
+ Iterable supportedEngines();
}
diff --git a/server/src/main/java/org/neo4j/server/webadmin/rest/RootService.java b/server/src/main/java/org/neo4j/server/webadmin/rest/RootService.java
index 551eccb37..1f5639b6c 100644
--- a/server/src/main/java/org/neo4j/server/webadmin/rest/RootService.java
+++ b/server/src/main/java/org/neo4j/server/webadmin/rest/RootService.java
@@ -41,7 +41,7 @@ public Response getServiceDefinition( @Context UriInfo uriInfo, @Context OutputF
private AdvertisableService[] services()
{
- AdvertisableService console = new ConsoleService( (SessionFactory) null, null, null );
+ AdvertisableService console = new ConsoleService( (ConsoleSessionFactory) null, null, null );
AdvertisableService jmx = new JmxService( null, null );
MonitorService monitor = new MonitorService( null, null );
diff --git a/server/src/main/java/org/neo4j/server/webadmin/rest/SessionFactoryImpl.java b/server/src/main/java/org/neo4j/server/webadmin/rest/SessionFactoryImpl.java
index 02a6e3249..00654b4c8 100644
--- a/server/src/main/java/org/neo4j/server/webadmin/rest/SessionFactoryImpl.java
+++ b/server/src/main/java/org/neo4j/server/webadmin/rest/SessionFactoryImpl.java
@@ -19,6 +19,11 @@
*/
package org.neo4j.server.webadmin.rest;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
import javax.servlet.http.HttpSession;
import org.neo4j.server.database.Database;
@@ -26,30 +31,37 @@
import org.neo4j.server.webadmin.console.GremlinSession;
import org.neo4j.server.webadmin.console.ScriptSession;
-public class SessionFactoryImpl implements SessionFactory
+public class SessionFactoryImpl implements ConsoleSessionFactory
{
private HttpSession httpSession;
+ private Map engineCreators = new HashMap();
- public SessionFactoryImpl( HttpSession httpSession )
+ public SessionFactoryImpl( HttpSession httpSession, List supportedEngines )
{
this.httpSession = httpSession;
+
+ enableEngines(supportedEngines);
}
@Override
public ScriptSession createSession( String engineName, Database database )
{
- if ( engineName.equals( "shell" ) )
+ engineName = engineName.toLowerCase();
+ if(engineCreators.containsKey(engineName))
{
-// return new CypherSession( database.graph );
- return getOrInstantiateSession( database, "shellSession", SessionCreator.SHELL );
- }
- else
- {
- return getOrInstantiateSession( database, "consoleSession", SessionCreator.GREMLIN );
+ return getOrInstantiateSession( database, engineName + "-console-session", engineCreators.get(engineName));
}
+
+ throw new IllegalArgumentException("Unknown console engine '" + engineName + "'.");
+ }
+
+ @Override
+ public Iterable supportedEngines()
+ {
+ return engineCreators.keySet();
}
- private ScriptSession getOrInstantiateSession( Database database, String key, SessionCreator creator )
+ private ScriptSession getOrInstantiateSession( Database database, String key, ConsoleEngineCreator creator )
{
Object session = httpSession.getAttribute( key );
if ( session == null )
@@ -60,7 +72,7 @@ private ScriptSession getOrInstantiateSession( Database database, String key, Se
return (ScriptSession) session;
}
- private static enum SessionCreator
+ public static enum ConsoleEngineCreator
{
GREMLIN
{
@@ -89,4 +101,20 @@ ScriptSession newSession( Database database )
abstract ScriptSession newSession( Database database );
}
+
+
+ private void enableEngines(List supportedEngines)
+ {
+ for(String engineName : supportedEngines)
+ {
+ for(ConsoleEngineCreator creator : EnumSet.allOf(ConsoleEngineCreator.class))
+ {
+ if(creator.name().equalsIgnoreCase(engineName))
+ {
+ engineCreators.put(engineName.toLowerCase(), creator);
+ }
+ }
+ }
+ }
+
}
diff --git a/server/src/main/java/org/neo4j/server/webadmin/rest/representations/ConsoleServiceRepresentation.java b/server/src/main/java/org/neo4j/server/webadmin/rest/representations/ConsoleServiceRepresentation.java
new file mode 100644
index 000000000..e8f3d8662
--- /dev/null
+++ b/server/src/main/java/org/neo4j/server/webadmin/rest/representations/ConsoleServiceRepresentation.java
@@ -0,0 +1,43 @@
+/**
+ * Copyright (c) 2002-2012 "Neo Technology,"
+ * Network Engine for Objects in Lund AB [http://neotechnology.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.server.webadmin.rest.representations;
+
+import org.neo4j.server.rest.repr.ListRepresentation;
+import org.neo4j.server.rest.repr.MappingSerializer;
+
+public class ConsoleServiceRepresentation extends ServiceDefinitionRepresentation {
+
+ private Iterable engines;
+
+ public ConsoleServiceRepresentation(String basePath, Iterable engines)
+ {
+ super(basePath);
+ resourceUri( "exec", "" );
+ this.engines = engines;
+ }
+
+ @Override
+ public void serialize( MappingSerializer serializer )
+ {
+ super.serialize(serializer);
+ serializer.putList("engines", ListRepresentation.string(engines));
+ }
+
+}
diff --git a/server/src/main/resources/webadmin-html/js/lib/backbone.js b/server/src/main/resources/webadmin-html/js/lib/backbone.js
index 0c475d1b5..22ad9d832 100644
--- a/server/src/main/resources/webadmin-html/js/lib/backbone.js
+++ b/server/src/main/resources/webadmin-html/js/lib/backbone.js
@@ -879,7 +879,8 @@
if (!_ && (typeof require !== 'undefined')) _ = require('underscore')._;
// For Backbone's purposes, jQuery or Zepto owns the `$` variable.
- var $ = root.jQuery || root.Zepto;
+ // Commented out, because jQuery is sometimes not defined at this point
+ //var $ = root.jQuery || root.Zepto;
// Runs Backbone.js in *noConflict* mode, returning the `Backbone` variable
// to its previous owner. Returns a reference to this Backbone object.
diff --git a/server/src/main/resources/webadmin-html/js/lib/neo4js.js b/server/src/main/resources/webadmin-html/js/lib/neo4js.js
index 342a6d6f9..044b35ae2 100644
--- a/server/src/main/resources/webadmin-html/js/lib/neo4js.js
+++ b/server/src/main/resources/webadmin-html/js/lib/neo4js.js
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2002-2011 "Neo Technology,"
+ * Copyright (c) 2010-2012 "Neo Technology,"
* Network Engine for Objects in Lund AB [http://neotechnology.com]
*
* This file is part of Neo4j.
@@ -999,6 +999,11 @@ neo4j.services.ConsoleService=function(a){neo4j.Service.call(this,a)
_.extend(neo4j.services.ConsoleService.prototype,neo4j.Service.prototype);
neo4j.services.ConsoleService.prototype.exec=neo4j.Service.resourceFactory({resource:"exec",method:"POST",before:function(b,a){b({command:a[0],engine:a[1]},a[2])
}});
+neo4j.services.ConsoleService.prototype.availableEngines=function(a){this.serviceMethodPreflight(function(){function c(d){a(d.engines?d.engines:[])
+}function b(d){a(d)
+}this.get("/",null,c,b)
+})
+};
neo4j.services.JmxService=function(a){neo4j.Service.call(this,a);
this.kernelInstance=neo4j.cachedFunction(this.kernelInstance,0,2000)
};
@@ -1248,13 +1253,11 @@ return this.getServiceDefinition().then(function(b,d,c){a.web.get(b.relationship
}else{b(a)
}})
},getServiceDefinition:function(){if(typeof(this._serviceDefinitionPromise)==="undefined"){var a=this;
-this._serviceDefinitionPromise=this.getDiscoveryDocument().then(function(c,d,b){a.web.get(c.data,function(e){d(e)
-})
+this._serviceDefinitionPromise=this.getDiscoveryDocument().then(function(c,d,b){a.web.get(c.data,d,b)
})
}return this._serviceDefinitionPromise
},getDiscoveryDocument:function(){if(typeof(this._discoveryDocumentPromise)==="undefined"){var a=this;
-this._discoveryDocumentPromise=new neo4j.Promise(function(c,b){a.web.get(a.url,function(d){c(d)
-})
+this._discoveryDocumentPromise=new neo4j.Promise(function(c,b){a.web.get(a.url,c,b)
})
}return this._discoveryDocumentPromise
},get:function(c,b,d,a){this.web.get(this.url+c,b,d,a)
diff --git a/server/src/test/java/org/neo4j/server/webadmin/rest/representations/ServerRootRepresentationTest.java b/server/src/test/java/org/neo4j/server/webadmin/rest/representations/ServerRootRepresentationTest.java
index 0ce91fe42..d1c1f9fab 100644
--- a/server/src/test/java/org/neo4j/server/webadmin/rest/representations/ServerRootRepresentationTest.java
+++ b/server/src/test/java/org/neo4j/server/webadmin/rest/representations/ServerRootRepresentationTest.java
@@ -28,14 +28,14 @@
import org.junit.Test;
import org.neo4j.server.webadmin.rest.ConsoleService;
-import org.neo4j.server.webadmin.rest.SessionFactory;
+import org.neo4j.server.webadmin.rest.ConsoleSessionFactory;
public class ServerRootRepresentationTest
{
@Test
public void shouldProvideAListOfServiceUris() throws Exception
{
- ConsoleService consoleService = new ConsoleService( (SessionFactory) null, null, null );
+ ConsoleService consoleService = new ConsoleService( (ConsoleSessionFactory) null, null, null );
ServerRootRepresentation srr = new ServerRootRepresentation( new URI( "http://example.org:9999" ),
consoleService );
Map> map = srr.serialize();
diff --git a/server/src/webtest/java/org/neo4j/server/webadmin/AbstractExclusiveServerWebadminTest.java b/server/src/webtest/java/org/neo4j/server/webadmin/AbstractExclusiveServerWebadminTest.java
new file mode 100644
index 000000000..e3dc20fe2
--- /dev/null
+++ b/server/src/webtest/java/org/neo4j/server/webadmin/AbstractExclusiveServerWebadminTest.java
@@ -0,0 +1,51 @@
+/**
+ * Copyright (c) 2002-2012 "Neo Technology,"
+ * Network Engine for Objects in Lund AB [http://neotechnology.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.server.webadmin;
+
+import org.apache.commons.lang.StringUtils;
+import org.neo4j.server.NeoServer;
+import org.neo4j.server.webdriver.WebDriverFacade;
+import org.neo4j.server.webdriver.WebadminWebdriverLibrary;
+import org.neo4j.test.server.ExclusiveServerTestBase;
+
+public abstract class AbstractExclusiveServerWebadminTest extends ExclusiveServerTestBase {
+
+ protected static WebadminWebdriverLibrary wl;
+
+ private static WebDriverFacade webdriverFacade;
+
+ public static void setupWebdriver(NeoServer server) throws Exception {
+ webdriverFacade = new WebDriverFacade();
+ wl = new WebadminWebdriverLibrary( webdriverFacade, deriveBaseUri(server) );
+ }
+
+ public static void shutdownWebdriver() throws Exception {
+ webdriverFacade.quitBrowser();
+ }
+
+ private static String deriveBaseUri(NeoServer server)
+ {
+ String overrideBaseUri = System.getProperty( "webdriver.override.neo-server.baseuri" );
+ if ( StringUtils.isNotEmpty( overrideBaseUri )) {
+ return overrideBaseUri;
+ }
+ return server.baseUri().toString();
+ }
+}
diff --git a/server/src/webtest/java/org/neo4j/server/webadmin/DiscoverAvailableConsolesWebIT.java b/server/src/webtest/java/org/neo4j/server/webadmin/DiscoverAvailableConsolesWebIT.java
new file mode 100644
index 000000000..ce7ad0c53
--- /dev/null
+++ b/server/src/webtest/java/org/neo4j/server/webadmin/DiscoverAvailableConsolesWebIT.java
@@ -0,0 +1,91 @@
+/**
+ * Copyright (c) 2002-2012 "Neo Technology,"
+ * Network Engine for Objects in Lund AB [http://neotechnology.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.server.webadmin;
+
+import static org.neo4j.server.helpers.ServerBuilder.server;
+
+import org.junit.Test;
+import org.neo4j.server.NeoServer;
+import org.neo4j.server.configuration.Configurator;
+import org.openqa.selenium.By;
+
+public class DiscoverAvailableConsolesWebIT extends AbstractExclusiveServerWebadminTest {
+
+ @Test
+ public void shouldShowBothGremlinAndShellIfAvailable() throws Exception {
+ NeoServer server = server().build();
+ try {
+ server.start();
+ setupWebdriver(server);
+
+ wl.goToWebadminStartPage();
+ wl.clickOnTab("Console");
+ wl.waitForElementToAppear(By
+ .xpath("//div[@id='console-tabs']//a[contains(.,'Gremlin')]"));
+ wl.waitForElementToAppear(By
+ .xpath("//div[@id='console-tabs']//a[contains(.,'Neo4j Shell')]"));
+
+ } finally {
+ shutdownWebdriver();
+ server.stop();
+ }
+ }
+
+ @Test
+ public void shouldNotShowGremlinIfNotAvailable() throws Exception {
+ NeoServer server = server().withProperty(Configurator.MANAGEMENT_CONSOLE_ENGINES, "shell").build();
+ try {
+ server.start();
+ setupWebdriver(server);
+
+ wl.goToWebadminStartPage();
+ wl.clickOnTab("Console");
+ wl.waitForElementToDisappear(By
+ .xpath("//div[@id='console-tabs']//a[contains(.,'Gremlin')]"));
+ wl.waitForElementToAppear(By
+ .xpath("//div[@id='console-tabs']//a[contains(.,'Neo4j Shell')]"));
+
+ } finally {
+ shutdownWebdriver();
+ server.stop();
+ }
+ }
+
+ @Test
+ public void shouldNotShowEitherShellIfBothAreDisabled() throws Exception {
+ NeoServer server = server().withProperty(Configurator.MANAGEMENT_CONSOLE_ENGINES, "").build();
+ try {
+ server.start();
+ setupWebdriver(server);
+
+ wl.goToWebadminStartPage();
+ wl.clickOnTab("Console");
+ wl.waitForElementToDisappear(By
+ .xpath("//div[@id='console-tabs']//a[contains(.,'Gremlin')]"));
+ wl.waitForElementToDisappear(By
+ .xpath("//div[@id='console-tabs']//a[contains(.,'Neo4j Shell')]"));
+
+ } finally {
+ shutdownWebdriver();
+ server.stop();
+ }
+ }
+
+}