Merge branch 'develop' of https://github.com/vivo-project/Vitro into develop

This commit is contained in:
brianjlowe 2013-12-02 15:55:27 -05:00
commit e822dfb3dc
12 changed files with 239 additions and 128 deletions

View file

@ -16,7 +16,7 @@
<auth:status rdf:datatype="http://www.w3.org/2001/XMLSchema#string">ACTIVE</auth:status>
<auth:loginCount rdf:datatype="http://www.w3.org/2001/XMLSchema#int">1</auth:loginCount>
<auth:passwordLinkExpires rdf:datatype="http://www.w3.org/2001/XMLSchema#long">0</auth:passwordLinkExpires>
<auth:hasPermissionSet rdf:resource="http://permissionSet-50"/>
<auth:hasPermissionSet rdf:resource="http://vitro.mannlib.cornell.edu/ns/vitro/authorization#ADMIN"/>
</auth:UserAccount>
<auth:UserAccount rdf:about="http://vivo.mydomain.edu/individual/JohnCurator">
@ -28,7 +28,7 @@
<auth:status rdf:datatype="http://www.w3.org/2001/XMLSchema#string">ACTIVE</auth:status>
<auth:loginCount rdf:datatype="http://www.w3.org/2001/XMLSchema#int">1</auth:loginCount>
<auth:passwordLinkExpires rdf:datatype="http://www.w3.org/2001/XMLSchema#long">0</auth:passwordLinkExpires>
<auth:hasPermissionSet rdf:resource="http://permissionSet-5"/>
<auth:hasPermissionSet rdf:resource="http://vitro.mannlib.cornell.edu/ns/vitro/authorization#CURATOR"/>
</auth:UserAccount>
<auth:UserAccount rdf:about="http://vivo.mydomain.edu/individual/SallyEditor">
@ -40,7 +40,7 @@
<auth:status rdf:datatype="http://www.w3.org/2001/XMLSchema#string">ACTIVE</auth:status>
<auth:loginCount rdf:datatype="http://www.w3.org/2001/XMLSchema#int">1</auth:loginCount>
<auth:passwordLinkExpires rdf:datatype="http://www.w3.org/2001/XMLSchema#long">0</auth:passwordLinkExpires>
<auth:hasPermissionSet rdf:resource="http://permissionSet-4"/>
<auth:hasPermissionSet rdf:resource="http://vitro.mannlib.cornell.edu/ns/vitro/authorization#EDITOR"/>
</auth:UserAccount>
<auth:UserAccount rdf:about="http://vivo.mydomain.edu/individual/JoeUser">
@ -52,7 +52,7 @@
<auth:status rdf:datatype="http://www.w3.org/2001/XMLSchema#string">ACTIVE</auth:status>
<auth:loginCount rdf:datatype="http://www.w3.org/2001/XMLSchema#int">1</auth:loginCount>
<auth:passwordLinkExpires rdf:datatype="http://www.w3.org/2001/XMLSchema#long">0</auth:passwordLinkExpires>
<auth:hasPermissionSet rdf:resource="http://permissionSet-1"/>
<auth:hasPermissionSet rdf:resource="http://vitro.mannlib.cornell.edu/ns/vitro/authorization#SELF_EDITOR"/>
</auth:UserAccount>
</rdf:RDF>

View file

@ -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 ;
@ -24,7 +25,11 @@ auth:ADMIN
auth:hasPermission simplePermission:UseMiscellaneousAdminPages ;
auth:hasPermission simplePermission:UseSparqlQueryPage ;
auth:hasPermission simplePermission:PageViewableAdmin ;
auth:hasPermission simplePermission:EnableDeveloperPanel ;
# 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 ;

View file

@ -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

View file

@ -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;

View file

@ -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:
* <ul>
* <li>No redirecting to the login page if not authorized</li>
* <li>No redirecting to the home page on insufficient authorization</li>
* <li>No support for GET or HEAD requests, only POST.</li>
* </ul>
*
* So these responses will be produced:
*
* <pre>
* 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
* </pre>
*/
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("<H1>" + statusCode + " " + message + "</H1>");
}
private void doResponse(HttpServletResponse resp, int statusCode,
String message, Throwable e) throws IOException {
resp.setStatus(statusCode);
PrintWriter writer = resp.getWriter();
writer.println("<H1>" + statusCode + " " + message + "</H1>");
writer.println("<pre>");
e.printStackTrace(writer);
writer.println("</pre>");
}
// ----------------------------------------------------------------------
// 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);
}
}
}

View file

@ -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;
}
}

View file

@ -537,13 +537,14 @@ public class PageDaoJena extends JenaBaseDao implements PageDao {
List<String> actions = new ArrayList<String>();
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;
}

View file

@ -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<PathPieces> getMatches() {

View file

@ -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);
}

View file

@ -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<String,Object> getData( Map<String, Object> valueMap ) {
HashMap<String, Object> data = new HashMap<String,Object>();
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;
}
}

View file

@ -1025,6 +1025,16 @@
<url-pattern>/admin/sparqlquery</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>SparqlUpdateApi</servlet-name>
<servlet-class>edu.cornell.mannlib.vitro.webapp.controller.api.SparqlUpdateApiController</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>SparqlUpdateApi</servlet-name>
<url-pattern>/api/sparqlUpdate</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>primitiveRdfEdit</servlet-name>
<servlet-class>edu.cornell.mannlib.vitro.webapp.controller.edit.PrimitiveRdfEdit</servlet-class>

View file

@ -1,12 +0,0 @@
<#-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
<h3>SPARQL Update Test</h3>
<p>This is an expermental SPARQL update service.</p>
<form action="${urls.base}/sparqlUpdateTest" method="post">
<p>
<textarea name="update" rows="20" cols="80" ></textarea>
<input type="submit" />
</p>
</form>