NIHVIVO-2279 convert UserAccountsAjaxController from its first version, which was essentially a stub.
This commit is contained in:
parent
f86f8e893c
commit
555e068d6a
3 changed files with 354 additions and 73 deletions
|
@ -0,0 +1,128 @@
|
|||
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||
|
||||
package edu.cornell.mannlib.vitro.webapp.controller.accounts.admin.ajax;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import edu.cornell.mannlib.vitro.webapp.beans.Individual;
|
||||
import edu.cornell.mannlib.vitro.webapp.beans.SelfEditingConfiguration;
|
||||
import edu.cornell.mannlib.vitro.webapp.beans.UserAccount;
|
||||
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
|
||||
import edu.cornell.mannlib.vitro.webapp.controller.accounts.admin.ajax.UserAccountsAjaxController.AjaxResponder;
|
||||
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.UrlBuilder;
|
||||
|
||||
/**
|
||||
* What is our reaction to this possible External Auth ID?
|
||||
*
|
||||
* Is somebody already using it (other than ourselves)? Does it match an
|
||||
* existing Profile? Neither?
|
||||
*
|
||||
* If the userAccountUri or the externalAuthId is empty, or if there is any
|
||||
* error, say "neither".
|
||||
*/
|
||||
class ExternalAuthChecker extends AjaxResponder {
|
||||
private static final Log log = LogFactory.getLog(ExternalAuthChecker.class);
|
||||
|
||||
private static final String PARAMETER_USER_ACCOUNT_URI = "userAccountUri";
|
||||
private static final String PARAMETER_ETERNAL_AUTH_ID = "externalAuthId";
|
||||
private static final String RESPONSE_ID_IN_USE = "idInUse";
|
||||
private static final String RESPONSE_MATCHES_PROFILE = "matchesProfile";
|
||||
private static final String RESPONSE_PROFILE_URI = "profileUri";
|
||||
private static final String RESPONSE_PROFILE_URL = "profileUrl";
|
||||
private static final String RESPONSE_PROFILE_LABEL = "profileLabel";
|
||||
|
||||
private final String userAccountUri;
|
||||
private final String externalAuthId;
|
||||
|
||||
private Individual matchingProfile;
|
||||
|
||||
public ExternalAuthChecker(HttpServlet parent, VitroRequest vreq,
|
||||
HttpServletResponse resp) {
|
||||
super(parent, vreq, resp);
|
||||
userAccountUri = getStringParameter(PARAMETER_USER_ACCOUNT_URI, "");
|
||||
externalAuthId = getStringParameter(PARAMETER_ETERNAL_AUTH_ID, "");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String prepareResponse() throws IOException, JSONException {
|
||||
if (someoneElseIsUsingThisExternalAuthId()) {
|
||||
return respondExternalAuthIdAlreadyUsed();
|
||||
}
|
||||
|
||||
checkForMatchingProfile();
|
||||
if (matchingProfile != null) {
|
||||
return respondWithMatchingProfile();
|
||||
}
|
||||
|
||||
return EMPTY_RESPONSE;
|
||||
}
|
||||
|
||||
private boolean someoneElseIsUsingThisExternalAuthId() {
|
||||
if (externalAuthId.isEmpty()) {
|
||||
log.debug("externalAuthId is empty.");
|
||||
return false;
|
||||
}
|
||||
|
||||
UserAccount user = uaDao.getUserAccountByExternalAuthId(externalAuthId);
|
||||
if (user == null) {
|
||||
log.debug("no user account has externalAuthId='" + externalAuthId
|
||||
+ "'");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (user.getUri().equals(userAccountUri)) {
|
||||
log.debug("externalAuthId '" + externalAuthId
|
||||
+ "' belongs to current user '" + userAccountUri + "'");
|
||||
return false;
|
||||
}
|
||||
|
||||
log.debug(user.getEmailAddress() + " is already using externalAuthId '"
|
||||
+ externalAuthId + "'");
|
||||
return true;
|
||||
}
|
||||
|
||||
private String respondExternalAuthIdAlreadyUsed() throws JSONException {
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
jsonObject.put(RESPONSE_ID_IN_USE, true);
|
||||
return jsonObject.toString();
|
||||
}
|
||||
|
||||
private void checkForMatchingProfile() {
|
||||
if (userAccountUri.isEmpty()) {
|
||||
log.debug("userAccountUri is empty");
|
||||
return;
|
||||
}
|
||||
|
||||
List<Individual> inds = SelfEditingConfiguration.getBean(vreq)
|
||||
.getAssociatedIndividuals(indDao, externalAuthId);
|
||||
if (inds.isEmpty()) {
|
||||
log.debug("No profiles match '" + externalAuthId + "'");
|
||||
return;
|
||||
}
|
||||
|
||||
this.matchingProfile = inds.get(0);
|
||||
}
|
||||
|
||||
private String respondWithMatchingProfile() throws JSONException {
|
||||
String uri = matchingProfile.getURI();
|
||||
String url = UrlBuilder.getIndividualProfileUrl(uri, vreq);
|
||||
String label = matchingProfile.getRdfsLabel();
|
||||
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
jsonObject.put(RESPONSE_MATCHES_PROFILE, true);
|
||||
jsonObject.put(RESPONSE_PROFILE_URI, uri);
|
||||
jsonObject.put(RESPONSE_PROFILE_URL, url);
|
||||
jsonObject.put(RESPONSE_PROFILE_LABEL, label);
|
||||
return jsonObject.toString();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,191 @@
|
|||
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||
|
||||
package edu.cornell.mannlib.vitro.webapp.controller.accounts.admin.ajax;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
||||
import com.hp.hpl.jena.ontology.OntModel;
|
||||
import com.hp.hpl.jena.query.Query;
|
||||
import com.hp.hpl.jena.query.QueryExecution;
|
||||
import com.hp.hpl.jena.query.QueryExecutionFactory;
|
||||
import com.hp.hpl.jena.query.QueryFactory;
|
||||
import com.hp.hpl.jena.query.QuerySolution;
|
||||
import com.hp.hpl.jena.query.ResultSet;
|
||||
import com.hp.hpl.jena.query.Syntax;
|
||||
|
||||
import edu.cornell.mannlib.vitro.webapp.beans.SelfEditingConfiguration;
|
||||
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
|
||||
import edu.cornell.mannlib.vitro.webapp.controller.accounts.admin.ajax.UserAccountsAjaxController.AjaxResponder;
|
||||
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.UrlBuilder;
|
||||
|
||||
/**
|
||||
* Get a list of Profiles with last names that begin with this search term, and
|
||||
* that have no matching property, unless it matches the current externalAuthId.
|
||||
* (So a Profile that is currently associated with the user is not excluded from
|
||||
* the list.)
|
||||
*
|
||||
* For each such Profile, return the label, the URL and the URI.
|
||||
*
|
||||
* If the matching property is not defined, or if the search term is empty, or
|
||||
* if an error occurs, return an empty result.
|
||||
*/
|
||||
class ProfileAutoCompleter extends AjaxResponder {
|
||||
private static final Log log = LogFactory
|
||||
.getLog(ProfileAutoCompleter.class);
|
||||
|
||||
private static final String PARAMETER_SEARCH_TERM = "term";
|
||||
private static final String PARAMETER_ETERNAL_AUTH_ID = "externalAuthId";
|
||||
|
||||
private static final Syntax SYNTAX = Syntax.syntaxARQ;
|
||||
|
||||
private static final String QUERY_TEMPLATE = "" //
|
||||
+ "PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> \n"
|
||||
+ "PREFIX foaf: <http://xmlns.com/foaf/0.1/> \n" + "\n" //
|
||||
+ "SELECT DISTINCT ?uri ?fn ?ln \n" //
|
||||
+ "WHERE {\n" //
|
||||
+ " ?uri rdf:type foaf:Person ; \n" //
|
||||
+ " foaf:firstName ?fn ; \n" //
|
||||
+ " foaf:lastName ?ln . \n" //
|
||||
+ " OPTIONAL { ?uri <%matchingPropertyUri%> ?id} \n" //
|
||||
+ " FILTER ( !bound(?id) || (?id = '%externalAuthId%') ) \n" //
|
||||
+ " FILTER ( REGEX(?ln, '%searchTerm%', 'i') ) \n" //
|
||||
+ "} \n" //
|
||||
+ "ORDER BY ?ln ?fn \n" //
|
||||
+ "LIMIT 20 \n";
|
||||
|
||||
private final String term;
|
||||
private final String externalAuthId;
|
||||
private final String selfEditingIdMatchingProperty;
|
||||
private final OntModel fullModel;
|
||||
|
||||
public ProfileAutoCompleter(HttpServlet parent, VitroRequest vreq,
|
||||
HttpServletResponse resp) {
|
||||
super(parent, vreq, resp);
|
||||
term = getStringParameter(PARAMETER_SEARCH_TERM, "");
|
||||
externalAuthId = getStringParameter(PARAMETER_ETERNAL_AUTH_ID, "");
|
||||
|
||||
// TODO This seems to expose the matching property and mechanism too
|
||||
// much. Can this be done within SelfEditingConfiguration somehow?
|
||||
selfEditingIdMatchingProperty = SelfEditingConfiguration.getBean(vreq)
|
||||
.getMatchingPropertyUri();
|
||||
|
||||
fullModel = vreq.getJenaOntModel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String prepareResponse() throws IOException, JSONException {
|
||||
if (term.isEmpty()) {
|
||||
return EMPTY_RESPONSE;
|
||||
}
|
||||
if (selfEditingIdMatchingProperty == null) {
|
||||
return EMPTY_RESPONSE;
|
||||
}
|
||||
if (selfEditingIdMatchingProperty.isEmpty()) {
|
||||
return EMPTY_RESPONSE;
|
||||
}
|
||||
return doSparqlQueryAndParseResult();
|
||||
}
|
||||
|
||||
private String doSparqlQueryAndParseResult() throws JSONException {
|
||||
String queryStr = prepareQueryString();
|
||||
|
||||
QueryExecution qe = null;
|
||||
List<ProfileInfo> results;
|
||||
try {
|
||||
Query query = QueryFactory.create(queryStr, SYNTAX);
|
||||
qe = QueryExecutionFactory.create(query, fullModel);
|
||||
results = parseResults(qe.execSelect());
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to execute the query: " + queryStr, e);
|
||||
results = Collections.emptyList();
|
||||
} finally {
|
||||
if (qe != null) {
|
||||
qe.close();
|
||||
}
|
||||
}
|
||||
|
||||
JSONArray jsonArray = prepareJsonArray(results);
|
||||
return jsonArray.toString();
|
||||
}
|
||||
|
||||
private String prepareQueryString() {
|
||||
String queryString = QUERY_TEMPLATE
|
||||
.replace("%matchingPropertyUri%", selfEditingIdMatchingProperty)
|
||||
.replace("%searchTerm%", term)
|
||||
.replace("%externalAuthId%", externalAuthId);
|
||||
log.debug("Query string is '" + queryString + "'");
|
||||
return queryString;
|
||||
}
|
||||
|
||||
private List<ProfileInfo> parseResults(ResultSet results) {
|
||||
List<ProfileInfo> profiles = new ArrayList<ProfileInfo>();
|
||||
while (results.hasNext()) {
|
||||
QuerySolution solution = results.next();
|
||||
ProfileInfo pi = parseSolution(solution);
|
||||
profiles.add(pi);
|
||||
}
|
||||
log.debug("Results are: " + profiles);
|
||||
return profiles;
|
||||
}
|
||||
|
||||
private ProfileInfo parseSolution(QuerySolution solution) {
|
||||
String uri = solution.getResource("uri").getURI();
|
||||
String url = UrlBuilder.getIndividualProfileUrl(uri, vreq);
|
||||
|
||||
String firstName = solution.getLiteral("fn").getString();
|
||||
String lastName = solution.getLiteral("ln").getString();
|
||||
String label = lastName + ", " + firstName;
|
||||
|
||||
return new ProfileInfo(uri, url, label);
|
||||
}
|
||||
|
||||
private JSONArray prepareJsonArray(List<ProfileInfo> results)
|
||||
throws JSONException {
|
||||
JSONArray jsonArray = new JSONArray();
|
||||
|
||||
for (int i = 0; i < results.size(); i++) {
|
||||
ProfileInfo profile = results.get(i);
|
||||
|
||||
Map<String, String> map = new HashMap<String, String>();
|
||||
map.put("label", profile.label);
|
||||
map.put("uri", profile.uri);
|
||||
map.put("url", profile.url);
|
||||
|
||||
jsonArray.put(i, map);
|
||||
}
|
||||
|
||||
return jsonArray;
|
||||
}
|
||||
|
||||
private static class ProfileInfo {
|
||||
final String uri;
|
||||
final String url;
|
||||
final String label;
|
||||
|
||||
public ProfileInfo(String uri, String url, String label) {
|
||||
this.uri = uri;
|
||||
this.url = url;
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ProfileInfo[label=" + label + ", uri=" + uri + ", url="
|
||||
+ url + "]";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -8,18 +8,24 @@ import javax.servlet.ServletException;
|
|||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.Actions;
|
||||
import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.usepages.ManageUserAccounts;
|
||||
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
|
||||
import edu.cornell.mannlib.vitro.webapp.controller.ajax.VitroAjaxController;
|
||||
import edu.cornell.mannlib.vitro.webapp.dao.IndividualDao;
|
||||
import edu.cornell.mannlib.vitro.webapp.dao.UserAccountsDao;
|
||||
|
||||
/**
|
||||
* Handle the AJAX functions that are specific to the UserAccounts pages.
|
||||
*/
|
||||
public class UserAccountsAjaxController extends VitroAjaxController {
|
||||
private static final Log log = LogFactory
|
||||
.getLog(UserAccountsAjaxController.class);
|
||||
|
||||
private static final String PARAMETER_FUNCTION = "function";
|
||||
|
||||
@Override
|
||||
|
@ -30,27 +36,49 @@ public class UserAccountsAjaxController extends VitroAjaxController {
|
|||
@Override
|
||||
protected void doRequest(VitroRequest vreq, HttpServletResponse resp)
|
||||
throws ServletException, IOException {
|
||||
String function = vreq.getParameter(PARAMETER_FUNCTION);
|
||||
if ("checkExternalAuth".equals(function)) {
|
||||
new ExternalAuthChecker(this, vreq, resp).processRequest();
|
||||
} else {
|
||||
new ErrorResponder(this, vreq, resp).processRequest();
|
||||
try {
|
||||
String function = vreq.getParameter(PARAMETER_FUNCTION);
|
||||
if ("checkExternalAuth".equals(function)) {
|
||||
new ExternalAuthChecker(this, vreq, resp).processRequest();
|
||||
} else if ("autoCompleteProfile".equals(function)) {
|
||||
new ProfileAutoCompleter(this, vreq, resp).processRequest();
|
||||
} else {
|
||||
resp.getWriter().write("[]");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error(e, e);
|
||||
resp.getWriter().write("[]");
|
||||
}
|
||||
}
|
||||
|
||||
static abstract class AjaxResponder {
|
||||
protected static final String EMPTY_RESPONSE = "[]";
|
||||
|
||||
protected final HttpServlet parent;
|
||||
protected final VitroRequest vreq;
|
||||
protected final HttpServletResponse resp;
|
||||
protected final IndividualDao indDao;
|
||||
protected final UserAccountsDao uaDao;
|
||||
|
||||
public AjaxResponder(HttpServlet parent, VitroRequest vreq,
|
||||
HttpServletResponse resp) {
|
||||
this.parent = parent;
|
||||
this.vreq = vreq;
|
||||
this.resp = resp;
|
||||
this.indDao = vreq.getWebappDaoFactory().getIndividualDao();
|
||||
this.uaDao = vreq.getWebappDaoFactory().getUserAccountsDao();
|
||||
}
|
||||
|
||||
public abstract void processRequest() throws IOException;
|
||||
public final void processRequest() {
|
||||
try {
|
||||
resp.getWriter().write(prepareResponse());
|
||||
} catch (Exception e) {
|
||||
log.error("Problem with AJAX response", e);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract String prepareResponse() throws IOException,
|
||||
JSONException;
|
||||
|
||||
protected String getStringParameter(String key, String defaultValue) {
|
||||
String value = vreq.getParameter(key);
|
||||
|
@ -59,70 +87,4 @@ public class UserAccountsAjaxController extends VitroAjaxController {
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* What is our reaction to this possible External Auth ID?
|
||||
*
|
||||
* Is somebody already using it (other than ourselves)? Does it match an
|
||||
* existing Profile? Neither?
|
||||
*/
|
||||
private static class ExternalAuthChecker extends AjaxResponder {
|
||||
private static final String PARAMETER_USER_ACCOUNT_URI = "userAccountUri";
|
||||
private static final String PARAMETER_ETERNAL_AUTH_ID = "externalAuthId";
|
||||
private static final String RESPONSE_ID_IN_USE = "idInUse";
|
||||
private static final String RESPONSE_MATCHES_PROFILE = "matchesProfile";
|
||||
private static final String RESPONSE_PROFILE_URI = "profileUri";
|
||||
private static final String RESPONSE_PROFILE_URL = "profileUrl";
|
||||
private static final String RESPONSE_PROFILE_LABEL = "profileLabel";
|
||||
|
||||
private final String userAccountUri;
|
||||
private final String externalAuthId;
|
||||
|
||||
public ExternalAuthChecker(HttpServlet parent, VitroRequest vreq,
|
||||
HttpServletResponse resp) {
|
||||
super(parent, vreq, resp);
|
||||
userAccountUri = getStringParameter(PARAMETER_USER_ACCOUNT_URI, "");
|
||||
externalAuthId = getStringParameter(PARAMETER_ETERNAL_AUTH_ID, "");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processRequest() throws IOException {
|
||||
// TODO For now, a totally bogus response:
|
||||
// If "A", somebody else is already using the externalAuthId
|
||||
// If "B", matches "Joe Blow"
|
||||
// Anything else, no match.
|
||||
try {
|
||||
if ("A".equals(externalAuthId)) {
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
jsonObject.put(RESPONSE_ID_IN_USE, true);
|
||||
resp.getWriter().write(jsonObject.toString());
|
||||
} else if ("B".equals(externalAuthId)) {
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
jsonObject.put(RESPONSE_MATCHES_PROFILE, true);
|
||||
jsonObject.put(RESPONSE_PROFILE_URI,
|
||||
"http://some.bogus.profile");
|
||||
jsonObject.put(RESPONSE_PROFILE_URL,
|
||||
"http://some.bogus.profileUrl");
|
||||
jsonObject.put(RESPONSE_PROFILE_LABEL, "bogus label");
|
||||
resp.getWriter().write(jsonObject.toString());
|
||||
} else {
|
||||
resp.getWriter().write("[]");
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
resp.getWriter().write("[]");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class ErrorResponder extends AjaxResponder {
|
||||
public ErrorResponder(HttpServlet parent, VitroRequest vreq,
|
||||
HttpServletResponse resp) {
|
||||
super(parent, vreq, resp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processRequest() throws IOException {
|
||||
resp.getWriter().write("[]");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue