diff --git a/utilities/testrunner/src/edu/cornell/mannlib/vitro/utilities/testrunner/test-user-model.owl b/utilities/testrunner/src/edu/cornell/mannlib/vitro/utilities/testrunner/test-user-model.owl
index c6e9a3523..a84b13707 100644
--- a/utilities/testrunner/src/edu/cornell/mannlib/vitro/utilities/testrunner/test-user-model.owl
+++ b/utilities/testrunner/src/edu/cornell/mannlib/vitro/utilities/testrunner/test-user-model.owl
@@ -16,7 +16,7 @@
ACTIVE
1
0
-
+
@@ -28,7 +28,7 @@
ACTIVE
1
0
-
+
@@ -40,7 +40,7 @@
ACTIVE
1
0
-
+
@@ -52,7 +52,7 @@
ACTIVE
1
0
-
+
diff --git a/webapp/rdf/auth/everytime/permission_config.n3 b/webapp/rdf/auth/everytime/permission_config.n3
index c4a88018a..e0b1c1960 100644
--- a/webapp/rdf/auth/everytime/permission_config.n3
+++ b/webapp/rdf/auth/everytime/permission_config.n3
@@ -12,6 +12,7 @@ auth:ADMIN
# ADMIN-only permissions
auth:hasPermission simplePermission:AccessSpecialDataModels ;
+ auth:hasPermission simplePermission:EnableDeveloperPanel ;
auth:hasPermission simplePermission:LoginDuringMaintenance ;
auth:hasPermission simplePermission:ManageMenus ;
auth:hasPermission simplePermission:ManageProxies ;
@@ -23,8 +24,12 @@ auth:ADMIN
auth:hasPermission simplePermission:UseAdvancedDataToolsPages ;
auth:hasPermission simplePermission:UseMiscellaneousAdminPages ;
auth:hasPermission simplePermission:UseSparqlQueryPage ;
- auth:hasPermission simplePermission:PageViewableAdmin ;
- auth:hasPermission simplePermission:EnableDeveloperPanel ;
+ auth:hasPermission simplePermission:PageViewableAdmin ;
+
+ # Uncomment the following permission line to enable the SPARQL update API.
+ # Before enabling, be sure that the URL api/sparqlUpdate is secured by SSH,
+ # so passwords will not be sent in clear text.
+# auth:hasPermission simplePermission:UseSparqlUpdateApi ;
# permissions for CURATOR and above.
auth:hasPermission simplePermission:EditOntology ;
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/permissions/SimplePermission.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/permissions/SimplePermission.java
index 33c609c5b..0484638bd 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/permissions/SimplePermission.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/permissions/SimplePermission.java
@@ -38,6 +38,8 @@ public class SimplePermission extends Permission {
NAMESPACE + "EditOwnAccount");
public static final SimplePermission EDIT_SITE_INFORMATION = new SimplePermission(
NAMESPACE + "EditSiteInformation");
+ public static final SimplePermission ENABLE_DEVELOPER_PANEL = new SimplePermission(
+ NAMESPACE + "EnableDeveloperPanel");
public static final SimplePermission LOGIN_DURING_MAINTENANCE = new SimplePermission(
NAMESPACE + "LoginDuringMaintenance");
public static final SimplePermission MANAGE_MENUS = new SimplePermission(
@@ -76,9 +78,8 @@ public class SimplePermission extends Permission {
NAMESPACE + "UseAdvancedDataToolsPages");
public static final SimplePermission USE_SPARQL_QUERY_PAGE = new SimplePermission(
NAMESPACE + "UseSparqlQueryPage");
- public static final SimplePermission ENABLE_DEVELOPER_PANEL = new SimplePermission(
- NAMESPACE + "EnableDeveloperPanel");
-
+ public static final SimplePermission USE_SPARQL_UPDATE_API = new SimplePermission(
+ NAMESPACE + "UseSparqlUpdateApi");
// ----------------------------------------------------------------------
// These instances are "catch all" permissions to cover poorly defined
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/policy/PolicyHelper.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/policy/PolicyHelper.java
index d8a606efb..b1250a855 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/policy/PolicyHelper.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/policy/PolicyHelper.java
@@ -83,11 +83,11 @@ public class PolicyHelper {
}
try{
- Authenticator basicAuth = new BasicAuthenticator(req);
- UserAccount user = basicAuth.getAccountForInternalAuth( email );
+ Authenticator auth = Authenticator.getInstance(req);
+ UserAccount user = auth.getAccountForInternalAuth( email );
log.debug("userAccount is " + user==null?"null":user.getUri() );
- if( ! basicAuth.isCurrentPassword( user, password ) ){
+ if( ! auth.isCurrentPassword( user, password ) ){
log.debug(String.format("UNAUTHORIZED, password not accepted for %s, account URI: %s",
user.getEmailAddress(), user.getUri()));
return false;
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/api/SparqlUpdateApiController.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/api/SparqlUpdateApiController.java
new file mode 100644
index 000000000..60a0e6af0
--- /dev/null
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/api/SparqlUpdateApiController.java
@@ -0,0 +1,201 @@
+/* $This file is distributed under the terms of the license in /doc/license.txt$ */
+
+package edu.cornell.mannlib.vitro.webapp.controller.api;
+
+import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
+import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN;
+import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
+import static javax.servlet.http.HttpServletResponse.SC_OK;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import com.hp.hpl.jena.query.Dataset;
+import com.hp.hpl.jena.update.GraphStore;
+import com.hp.hpl.jena.update.GraphStoreFactory;
+import com.hp.hpl.jena.update.UpdateAction;
+import com.hp.hpl.jena.update.UpdateFactory;
+import com.hp.hpl.jena.update.UpdateRequest;
+
+import edu.cornell.mannlib.vitro.webapp.auth.permissions.SimplePermission;
+import edu.cornell.mannlib.vitro.webapp.auth.policy.PolicyHelper;
+import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.Actions;
+import edu.cornell.mannlib.vitro.webapp.beans.UserAccount;
+import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
+import edu.cornell.mannlib.vitro.webapp.controller.authenticate.Authenticator;
+import edu.cornell.mannlib.vitro.webapp.dao.jena.RDFServiceDataset;
+import edu.cornell.mannlib.vitro.webapp.search.indexing.IndexBuilder;
+
+/**
+ * This extends HttpServlet instead of VitroHttpServlet because we want to have
+ * full control over the response:
+ *
+ * - No redirecting to the login page if not authorized
+ * - No redirecting to the home page on insufficient authorization
+ * - No support for GET or HEAD requests, only POST.
+ *
+ *
+ * So these responses will be produced:
+ *
+ *
+ * 200 Success
+ * 400 Failed to parse SPARQL update
+ * 400 SPARQL update must specify a GRAPH URI.
+ * 403 username/password combination is not valid
+ * 403 Account is not authorized
+ * 405 Method not allowed
+ * 500 Unknown error
+ *
+ */
+public class SparqlUpdateApiController extends HttpServlet {
+ private static final Log log = LogFactory
+ .getLog(SparqlUpdateApiController.class);
+
+ private static final Actions REQUIRED_ACTIONS = SimplePermission.USE_SPARQL_UPDATE_API.ACTIONS;
+
+ @Override
+ protected void doPost(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+ try {
+ checkAuthorization(req);
+ UpdateRequest parsed = parseUpdateString(req);
+ executeUpdate(req, parsed);
+ do200response(resp);
+ } catch (AuthException e) {
+ do403response(resp, e);
+ } catch (ParseException e) {
+ do400response(resp, e);
+ } catch (Exception e) {
+ do500response(resp, e);
+ }
+ }
+
+ private void checkAuthorization(HttpServletRequest req)
+ throws AuthException {
+ String email = req.getParameter("email");
+ String password = req.getParameter("password");
+
+ Authenticator auth = Authenticator.getInstance(req);
+ UserAccount account = auth.getAccountForInternalAuth(email);
+ if (!auth.isCurrentPassword(account, password)) {
+ log.debug("Invalid: '" + email + "'/'" + password + "'");
+ throw new AuthException("email/password combination is not valid");
+ }
+
+ if (!PolicyHelper.isAuthorizedForActions(req, email, password,
+ REQUIRED_ACTIONS)) {
+ log.debug("Not authorized: '" + email + "'");
+ throw new AuthException("Account is not authorized");
+ }
+
+ log.debug("Authorized for '" + email + "'");
+ }
+
+ private UpdateRequest parseUpdateString(HttpServletRequest req)
+ throws ParseException {
+ String update = req.getParameter("update");
+ if (StringUtils.isBlank(update)) {
+ log.debug("No update parameter.");
+ throw new ParseException("No 'update' parameter.");
+ }
+
+ if (!StringUtils.containsIgnoreCase(update, "GRAPH")) {
+ if (log.isDebugEnabled()) {
+ log.debug("No GRAPH uri in '" + update + "'");
+ }
+ throw new ParseException("SPARQL update must specify a GRAPH URI.");
+ }
+
+ try {
+ return UpdateFactory.create(update);
+ } catch (Exception e) {
+ log.debug("Problem parsing", e);
+ throw new ParseException("Failed to parse SPARQL update", e);
+ }
+ }
+
+ private void executeUpdate(HttpServletRequest req, UpdateRequest parsed) {
+ ServletContext ctx = req.getSession().getServletContext();
+ VitroRequest vreq = new VitroRequest(req);
+
+ IndexBuilder.getBuilder(ctx).pause();
+ try {
+ Dataset ds = new RDFServiceDataset(vreq.getUnfilteredRDFService());
+ GraphStore graphStore = GraphStoreFactory.create(ds);
+ UpdateAction.execute(parsed, graphStore);
+ } finally {
+ IndexBuilder.getBuilder(ctx).unpause();
+ }
+ }
+
+ private void do200response(HttpServletResponse resp) throws IOException {
+ doResponse(resp, SC_OK, "SPARQL update accepted.");
+ }
+
+ private void do403response(HttpServletResponse resp, AuthException e)
+ throws IOException {
+ doResponse(resp, SC_FORBIDDEN, e.getMessage());
+ }
+
+ private void do400response(HttpServletResponse resp, ParseException e)
+ throws IOException {
+ if (e.getCause() == null) {
+ doResponse(resp, SC_BAD_REQUEST, e.getMessage());
+ } else {
+ doResponse(resp, SC_BAD_REQUEST, e.getMessage(), e.getCause());
+ }
+ }
+
+ private void do500response(HttpServletResponse resp, Exception e)
+ throws IOException {
+ doResponse(resp, SC_INTERNAL_SERVER_ERROR, "Unknown error", e);
+ }
+
+ private void doResponse(HttpServletResponse resp, int statusCode,
+ String message) throws IOException {
+ resp.setStatus(statusCode);
+ PrintWriter writer = resp.getWriter();
+ writer.println("" + statusCode + " " + message + "
");
+ }
+
+ private void doResponse(HttpServletResponse resp, int statusCode,
+ String message, Throwable e) throws IOException {
+ resp.setStatus(statusCode);
+ PrintWriter writer = resp.getWriter();
+ writer.println("" + statusCode + " " + message + "
");
+ writer.println("");
+ e.printStackTrace(writer);
+ writer.println("
");
+ }
+
+ // ----------------------------------------------------------------------
+ // Helper classes
+ // ----------------------------------------------------------------------
+
+ private static class AuthException extends Exception {
+ private AuthException(String message) {
+ super(message);
+ }
+ }
+
+ private static class ParseException extends Exception {
+ private ParseException(String message) {
+ super(message);
+ }
+
+ private ParseException(String message, Throwable cause) {
+ super(message, cause);
+ }
+ }
+
+}
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/PageController.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/PageController.java
index 785bb031b..fe08c7d95 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/PageController.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/PageController.java
@@ -58,15 +58,16 @@ public class PageController extends FreemarkerHttpServlet{
if( pageActs == null && dgActs == null){
return Actions.AUTHORIZED;
- }else if( pageActs == null && dgActs != null ){
+ }else if( pageActs == null ){
return dgActs;
+ }else if( dgActs == null ){
+ return pageActs;
}else{
- return pageActs;
+ return pageActs.and(dgActs);
}
} catch (Exception e) {
- // TODO Auto-generated catch block
- log.debug(e);
+ log.warn(e);
return Actions.UNAUTHORIZED;
}
}
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/PageDaoJena.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/PageDaoJena.java
index 23e300013..fd333560b 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/PageDaoJena.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/PageDaoJena.java
@@ -537,13 +537,14 @@ public class PageDaoJena extends JenaBaseDao implements PageDao {
List actions = new ArrayList();
Model dModel = getOntModelSelector().getDisplayModel();
+ dModel.enterCriticalSection(false);
try{
QueryExecution qe =
QueryExecutionFactory.create( requiredActionsQuery, dModel, initialBindings);
actions = executeQueryToList( qe );
qe.close();
}finally{
- dModel.enterCriticalSection(false);
+ dModel.leaveCriticalSection();
}
return actions;
}
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/freemarker/loader/FreemarkerTemplateLoader.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/freemarker/loader/FreemarkerTemplateLoader.java
index 2ae556862..0bef38b30 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/freemarker/loader/FreemarkerTemplateLoader.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/freemarker/loader/FreemarkerTemplateLoader.java
@@ -285,7 +285,7 @@ public class FreemarkerTemplateLoader implements TemplateLoader {
}
public boolean fileQualifies(Path path) {
- return Files.isRegularFile(path) && Files.isReadable(path);
+ return Files.isReadable(path) && !Files.isDirectory(path);
}
public SortedSet getMatches() {
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/servlet/setup/ThemeInfoSetup.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/servlet/setup/ThemeInfoSetup.java
index 6b917c43f..5ccea3bfd 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/servlet/setup/ThemeInfoSetup.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/servlet/setup/ThemeInfoSetup.java
@@ -73,8 +73,8 @@ public class ThemeInfoSetup implements ServletContextListener {
ApplicationBean.themeInfo = new ThemeInfo(themesBaseDir,
defaultThemeName, themeNames);
- ss.info(this, ", current theme: " + currentThemeName
- + "default theme: " + defaultThemeName + ", available themes: "
+ ss.info(this, "current theme: " + currentThemeName
+ + ", default theme: " + defaultThemeName + ", available themes: "
+ themeNames);
}
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/dataGetter/SparqlUpdate.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/dataGetter/SparqlUpdate.java
deleted file mode 100644
index 7bb33194b..000000000
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/dataGetter/SparqlUpdate.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/* $This file is distributed under the terms of the license in /doc/license.txt$ */
-
-package edu.cornell.mannlib.vitro.webapp.utils.dataGetter;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import javax.servlet.ServletContext;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-import com.hp.hpl.jena.query.Dataset;
-import com.hp.hpl.jena.rdf.model.Model;
-import com.hp.hpl.jena.update.GraphStore;
-import com.hp.hpl.jena.update.GraphStoreFactory;
-import com.hp.hpl.jena.update.UpdateAction;
-
-import edu.cornell.mannlib.vitro.webapp.auth.permissions.SimplePermission;
-import edu.cornell.mannlib.vitro.webapp.auth.policy.PolicyHelper;
-import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.Actions;
-import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.ifaces.RequiresActions;
-import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
-import edu.cornell.mannlib.vitro.webapp.dao.jena.RDFServiceDataset;
-import edu.cornell.mannlib.vitro.webapp.search.indexing.IndexBuilder;
-
-/**
- * Handle a SPARQL Update request. This uses Jena ARQ and the RDFServiceDataset to
- * evaluate a SPARQL Update with the RDFService.
- *
- * The reason to make this a DataGettere was to allow configuration in RDF of this
- * service.
- */
-public class SparqlUpdate implements DataGetter, RequiresActions{
-
- private static final Log log = LogFactory.getLog(SparqlUpdate.class);
-
- VitroRequest vreq;
- ServletContext context;
-
- public SparqlUpdate(
- VitroRequest vreq, Model displayModel, String dataGetterURI ) {
- if( vreq == null )
- throw new IllegalArgumentException("VitroRequest may not be null.");
- this.vreq = vreq;
- this.context = vreq.getSession().getServletContext();
- }
-
-
- /**
- * Gets the update from the request and then executes it on
- * the RDFService.
- */
- @Override
- public Map getData( Map valueMap ) {
- HashMap data = new HashMap();
-
- String update = vreq.getParameter("update");
-
- if( update != null && !update.trim().isEmpty()){
- try{
- IndexBuilder.getBuilder(context).pause();
- Dataset ds = new RDFServiceDataset( vreq.getUnfilteredRDFService() );
- GraphStore graphStore = GraphStoreFactory.create(ds);
- log.warn("The SPARQL update is '"+vreq.getParameter("update")+"'");
- UpdateAction.parseExecute( vreq.getParameter("update") , graphStore );
- }finally{
- IndexBuilder.getBuilder(context).unpause();
- }
-
- }
-
- data.put("bodyTemplate", "page-sparqlUpdateTest.ftl");
- return data;
- }
-
-
- /**
- * Check if this request is authorized by the email/password.
- * If not do normal authorization.
- */
- @Override
- public Actions requiredActions(VitroRequest vreq) {
- String email = vreq.getParameter("email");
- String password = vreq.getParameter("password");
-
- boolean isAuth = PolicyHelper.isAuthorizedForActions(vreq,
- email, password, SimplePermission.MANAGE_SEARCH_INDEX.ACTIONS);
-
- if( isAuth )
- return Actions.AUTHORIZED;
- else
- return SimplePermission.MANAGE_SEARCH_INDEX.ACTIONS;
- }
-
-}
diff --git a/webapp/web/WEB-INF/web.xml b/webapp/web/WEB-INF/web.xml
index 3a5377bda..1d393a7b9 100644
--- a/webapp/web/WEB-INF/web.xml
+++ b/webapp/web/WEB-INF/web.xml
@@ -1025,6 +1025,16 @@
/admin/sparqlquery
+
+ SparqlUpdateApi
+ edu.cornell.mannlib.vitro.webapp.controller.api.SparqlUpdateApiController
+
+
+
+ SparqlUpdateApi
+ /api/sparqlUpdate
+
+
primitiveRdfEdit
edu.cornell.mannlib.vitro.webapp.controller.edit.PrimitiveRdfEdit
diff --git a/webapp/web/templates/freemarker/body/menupage/page-sparqlUpdateTest.ftl b/webapp/web/templates/freemarker/body/menupage/page-sparqlUpdateTest.ftl
deleted file mode 100644
index 17274f829..000000000
--- a/webapp/web/templates/freemarker/body/menupage/page-sparqlUpdateTest.ftl
+++ /dev/null
@@ -1,12 +0,0 @@
-<#-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
-
-SPARQL Update Test
-
-This is an expermental SPARQL update service.
-
-
\ No newline at end of file