Mercurial > hg > AnnotationManager
changeset 13:9393c9c9b916
saves annotations now!
author | casties |
---|---|
date | Wed, 21 Mar 2012 15:23:57 +0100 |
parents | 2e5d526079de |
children | 0f64de5fff5a |
files | .classpath WebContent/WEB-INF/web.xml.template src/de/mpiwg/itgroup/annotationManager/RDFHandling/Convert.java src/de/mpiwg/itgroup/annotationManager/restlet/AnnotatorAnnotations.java src/de/mpiwg/itgroup/annotationManager/restlet/AnnotatorResourceImpl.java src/de/mpiwg/itgroup/annotationManager/restlet/AnnotatorSearch.java src/de/mpiwg/itgroup/annotationManager/restlet/RestServer.java |
diffstat | 7 files changed, 226 insertions(+), 156 deletions(-) [+] |
line wrap: on
line diff
--- a/.classpath Wed Mar 21 11:09:20 2012 +0100 +++ b/.classpath Wed Mar 21 15:23:57 2012 +0100 @@ -42,7 +42,11 @@ <attribute name="owner.project.facets" value="jst.web"/> </attributes> </classpathentry> - <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/> - <classpathentry kind="lib" path="libs/joda-time-2.1.jar"/> + <classpathentry kind="lib" path="libs/joda-time-2.1.jar"> + <attributes> + <attribute name="org.eclipse.jst.component.dependency" value="/WEB-INF/lib"/> + </attributes> + </classpathentry> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.launching.macosx.MacOSXType/JDK 1.6"/> <classpathentry kind="output" path="build/classes"/> </classpath>
--- a/WebContent/WEB-INF/web.xml.template Wed Mar 21 11:09:20 2012 +0100 +++ b/WebContent/WEB-INF/web.xml.template Wed Mar 21 15:23:57 2012 +0100 @@ -4,38 +4,36 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> -<display-name>RESTfulJavaWebServices-Restlet</display-name> -<!-- Application classname --> -<context-param> -<param-name>org.restlet.application</param-name> -<param-value>de.mpiwg.itgroup.annotationManager.restlet.RestServer</param-value> -</context-param> - -<context-param> -<param-name>de.mpiwg.itgroup.annotationManager.jaas.configFilePath</param-name> -<param-value>file:///etc/jaasAuth.conf</param-value> -</context-param> - -<context-param> -<param-name>de.mpiwg.itgroup.annotationManager.virtuoso.tripleStoreUser</param-name> -<param-value>USERNAME</param-value> -</context-param> - -<context-param> -<param-name>de.mpiwg.itgroup.annotationManager.virtuoso.tripleStoreUserPassword</param-name> -<param-value>PASSWORD</param-value> -</context-param> -<!-- Restletadapter --> -<servlet> -<servlet-name>RestletServlet</servlet-name> -<servlet-class> -org.restlet.ext.servlet.ServerServlet -</servlet-class> -</servlet> - -<!-- Catchallrequests --> -<servlet-mapping> -<servlet-name>RestletServlet</servlet-name> -<url-pattern>/annotator/*</url-pattern> -</servlet-mapping> + <display-name>RESTfulJavaWebServices-Restlet</display-name> + <!-- Application classname --> + <context-param> + <param-name>org.restlet.application</param-name> + <param-value>de.mpiwg.itgroup.annotationManager.restlet.RestServer</param-value> + </context-param> + <context-param> + <param-name>de.mpiwg.itgroup.annotationManager.jaas.configFilePath</param-name> + <param-value>file:///etc/jaasAuth.conf</param-value> + </context-param> + <context-param> + <param-name>de.mpiwg.itgroup.annotationManager.virtuoso.tripleStoreUser</param-name> + <param-value>USERNAME</param-value> + </context-param> + <context-param> + <param-name>de.mpiwg.itgroup.annotationManager.virtuoso.tripleStoreUserPassword</param-name> + <param-value>PASSWORD</param-value> + </context-param> + <context-param> + <param-name>de.mpiwg.itgroup.annotationManager.virtuoso.tripleStoreConnectionURL</param-name> + <param-value>jdbc:virtuoso://virtuoso.mpiwg-berlin.mpg.de:1111</param-value> + </context-param> + <!-- Restletadapter --> + <servlet> + <servlet-name>RestletServlet</servlet-name> + <servlet-class>org.restlet.ext.servlet.ServerServlet</servlet-class> + </servlet> + <!-- Catchallrequests --> + <servlet-mapping> + <servlet-name>RestletServlet</servlet-name> + <url-pattern>/annotator/*</url-pattern> + </servlet-mapping> </web-app> \ No newline at end of file
--- a/src/de/mpiwg/itgroup/annotationManager/RDFHandling/Convert.java Wed Mar 21 11:09:20 2012 +0100 +++ b/src/de/mpiwg/itgroup/annotationManager/RDFHandling/Convert.java Wed Mar 21 15:23:57 2012 +0100 @@ -298,7 +298,10 @@ String user = context.getParameters().getFirstValue("de.mpiwg.itgroup.annotationManager.virtuoso.tripleStoreUser"); String pw = context.getParameters().getFirstValue("de.mpiwg.itgroup.annotationManager.virtuoso.tripleStoreUserPassword"); String connectionURL = context.getParameters().getFirstValue("de.mpiwg.itgroup.annotationManager.virtuoso.tripleStoreConnectionURL"); - + if (user == null || pw == null || connectionURL == null) { + logger.error(String.format("Missing triplestore parameters! (user=%s pw=%s url=%s)", user, pw, connectionURL)); + throw new TripleStoreStoreError(); + } //TripleStoreHandler th = new TripleStoreHandler("jdbc:virtuoso://virtuoso.mpiwg-berlin.mpg.de:1111", user, pw); TripleStoreHandler th = new TripleStoreHandler(connectionURL, user, pw);
--- a/src/de/mpiwg/itgroup/annotationManager/restlet/AnnotatorAnnotations.java Wed Mar 21 11:09:20 2012 +0100 +++ b/src/de/mpiwg/itgroup/annotationManager/restlet/AnnotatorAnnotations.java Wed Mar 21 15:23:57 2012 +0100 @@ -51,7 +51,7 @@ */ @Get("json") public Representation doGetJSON(Representation entity) { - + logger.debug("AnnotatorAnnotations doGetJSON!"); doOptions(entity); // TODO: what to do with authentication? boolean authenticated = isAuthenticated(entity); @@ -73,9 +73,9 @@ results = new JSONArray(); for (Convert.Annotation annot : annots) { - JSONObject jo = annot2AnnotatorJSON(annot); + JSONObject jo = createAnnotatorJson(annot); if (jo != null) { - results.put(annot2AnnotatorJSON(annot)); + results.put(createAnnotatorJson(annot)); } else { setStatus(Status.SERVER_ERROR_INTERNAL, "JSon Error"); return null; @@ -98,22 +98,27 @@ } /** - * POST with JSON content-type. json hash: username: name des users xpointer: xpointer auf den Ausschnitt (incl. der URL des + * POST with JSON content-type. + * + * json hash: username: name des users xpointer: xpointer auf den Ausschnitt (incl. der URL des * Dokumentes) text: text der annotation annoturl: url auf eine Annotation falls extern * * @return */ @Post("json") public Representation doPostJson(Representation entity) { - JsonRepresentation jrep; - Annotation annot; + logger.debug("AnnotatorAnnotations doPostJSON!"); + // set headers + setCorsHeaders(); + Annotation annot = null; try { - jrep = new JsonRepresentation(entity); + JsonRepresentation jrep = new JsonRepresentation(entity); JSONObject jo = jrep.getJsonObject(); if (jo == null) { setStatus(Status.SERVER_ERROR_INTERNAL); return null; } + // get Annotation object from posted JSON annot = createAnnotation(jo, entity); } catch (IOException e1) { setStatus(Status.SERVER_ERROR_INTERNAL); @@ -122,19 +127,21 @@ setStatus(Status.CLIENT_ERROR_BAD_REQUEST); return null; } - if (annot.xpointer == null || annot.creator == null) { + if (annot == null || annot.xpointer == null || annot.creator == null) { setStatus(Status.CLIENT_ERROR_BAD_REQUEST); return null; } - Annotation retVal; + Annotation storedAnnot; try { - retVal = new Convert("file:///annotations").storeAnnotation(annot); + // store Annotation + storedAnnot = new Convert("file:///annotations").storeAnnotation(annot); } catch (TripleStoreStoreError e) { e.printStackTrace(); setStatus(Status.SERVER_ERROR_INTERNAL, "TripleStore Error"); return null; } - JSONObject jo = annot2AnnotatorJSON(retVal); + // return stored annotation + JSONObject jo = createAnnotatorJson(storedAnnot); JsonRepresentation retRep = new JsonRepresentation(jo); return retRep; } @@ -147,6 +154,7 @@ */ @Post("html") public Representation doPostHtml(Representation entity) { + logger.debug("AnnotatorAnnotations doPostHtml!"); Convert.Annotation annot; annot = handleForm(entity); if (annot.xpointer == null || annot.creator == null) { @@ -177,16 +185,15 @@ return retRep; } - /** * - * @param entity should contain a form with the parameters "username", "password", "xpointer","text","uri","type" - * - * username,password is optional, if not given BasicAuthentification is used. + * @param entity + * should contain a form with the parameters "username", "password", "xpointer","text","uri","type" * - * + * username,password is optional, if not given BasicAuthentification is used. * - * If username given as a URI, the username will be transformed to an URI, username will be added to the MPIWG namespace defined in de.mpiwg.itgroup.annotationManager.Constants.NS + * If username given as a URI, the username will be transformed to an URI, username will be added to the MPIWG + * namespace defined in de.mpiwg.itgroup.annotationManager.Constants.NS * * @return */ @@ -213,7 +220,7 @@ User authUser = null; if (userFromForm == null) { - authUser = handleBasicAuthentification(entity); + authUser = getHttpAuthUser(entity); } // weder BasicAuth noch FormAuth @@ -228,17 +235,17 @@ username = authUser.getIdentifier(); } - //username should be a URI, if not it will set to the MPIWG namespace defined in de.mpiwg.itgroup.annotationManager.Constants.NS - String usernameOrig=username; + // username should be a URI, if not it will set to the MPIWG namespace defined in + // de.mpiwg.itgroup.annotationManager.Constants.NS + String usernameOrig = username; if (!username.startsWith("http")) - username=NS.MPIWG_PERSONS+username; - - if (mode.equals("complexAnnotation")){// Annotation mit text in externer ressource - + username = NS.MPIWG_PERSONS + username; + + if (mode.equals("complexAnnotation")) {// Annotation mit text in externer ressource + Context context = getContext(); String drupalPath = context.getParameters().getFirstValue("de.mpiwg.itgroup.annotationManager.drupalServer"); - - + AnnotationHandler ah = new AnnotationHandler(drupalPath); JSONObject newAnnot; try { @@ -249,7 +256,7 @@ return null; } try { - annot= new Convert.Annotation(xpointer, username, null, text, type, newAnnot.getString("node_uri")); + annot = new Convert.Annotation(xpointer, username, null, text, type, newAnnot.getString("node_uri")); } catch (JSONException e) { // TODO Auto-generated catch block e.printStackTrace(); @@ -257,8 +264,7 @@ return null; } } else - annot = new Convert.Annotation(xpointer, username, null, text, - type, url); + annot = new Convert.Annotation(xpointer, username, null, text, type, url); return annot; }
--- a/src/de/mpiwg/itgroup/annotationManager/restlet/AnnotatorResourceImpl.java Wed Mar 21 11:09:20 2012 +0100 +++ b/src/de/mpiwg/itgroup/annotationManager/restlet/AnnotatorResourceImpl.java Wed Mar 21 15:23:57 2012 +0100 @@ -20,6 +20,7 @@ import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; +import org.restlet.data.ClientInfo; import org.restlet.data.Form; import org.restlet.data.Status; import org.restlet.representation.Representation; @@ -45,12 +46,45 @@ } /** + * returns a hex String of a SHA256 digest of text. + * + * @param text + * @return + */ + public String getSha256Digest(String text) { + String digest = null; + try { + MessageDigest md = MessageDigest.getInstance("SHA-256"); + md.update(text.getBytes("UTF-8")); + byte[] dg = md.digest(); + StringBuffer sb = new StringBuffer(); + for (byte b : dg) { + sb.append(String.format("%02x", b)); + } + digest = sb.toString(); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + return digest; + } + + /** * Handle options request to allow CORS for AJAX. * * @param entity */ @Options public void doOptions(Representation entity) { + logger.debug("AnnotatorResourceImpl doOptions!"); + setCorsHeaders(); + } + + /** + * set headers to allow CORS for AJAX. + */ + protected void setCorsHeaders() { Form responseHeaders = (Form) getResponse().getAttributes().get("org.restlet.http.headers"); if (responseHeaders == null) { responseHeaders = new Form(); @@ -80,49 +114,42 @@ * @return */ public boolean isAuthenticated(Representation entity) { - // get authToken + return (checkAuthToken(entity) != null); + } + + /** + * checks Annotator Auth plugin authentication information from headers. returns userId if successful. + * + * @param entity + * @return + */ + public String checkAuthToken(Representation entity) { Form requestHeaders = (Form) getRequest().getAttributes().get("org.restlet.http.headers"); String consumerKey = requestHeaders.getFirstValue("x-annotator-consumer-key", true); if (consumerKey == null) { - return false; + return null; } + // get stored consumer secret for key RestServer restServer = (RestServer) getApplication(); String consumerSecret = restServer.getConsumerSecret(consumerKey); logger.debug("requested consumer key=" + consumerKey + " secret=" + consumerSecret); if (consumerSecret == null) { - return false; + return null; } String userId = requestHeaders.getFirstValue("x-annotator-user-id", true); String issueTime = requestHeaders.getFirstValue("x-annotator-auth-token-issue-time", true); if (userId == null || issueTime == null) { - return false; + return null; } // compute hashed token based on the values we know // computed_token = hashlib.sha256(consumer.secret + user_id + issue_time).hexdigest() - String computedToken; - try { - MessageDigest md = MessageDigest.getInstance("SHA-256"); - String computedString = consumerSecret + userId + issueTime; - md.update(computedString.getBytes("UTF-8")); - byte[] dg = md.digest(); - StringBuffer sb = new StringBuffer(); - for (byte b : dg) { - sb.append(String.format("%02x", b)); - } - computedToken = sb.toString(); - } catch (NoSuchAlgorithmException e) { - e.printStackTrace(); - return false; - } catch (UnsupportedEncodingException e) { - e.printStackTrace(); - return false; - } + String computedToken = getSha256Digest(consumerSecret + userId + issueTime); // compare to the token we got String authToken = requestHeaders.getFirstValue("x-annotator-auth-token", true); logger.debug(String.format("got: authToken=%s consumerSecret=%s userId=%s issueTime=%s", authToken, consumerSecret, userId, issueTime)); if (!computedToken.equals(authToken)) { - return false; + return null; } // check token lifetime // validity = iso8601.parse_date(issue_time) @@ -140,10 +167,10 @@ e.printStackTrace(); } if (tokenValidity == null || tokenValidity.isAfterNow() || tokenExpiry.isBeforeNow()) { - return false; + return null; } // must be ok then - return true; + return userId; } /** @@ -152,36 +179,45 @@ * @param annot * @return */ - public JSONObject annot2AnnotatorJSON(Convert.Annotation annot) { + public JSONObject createAnnotatorJson(Convert.Annotation annot) { + boolean makeUserObject = true; JSONObject jo = new JSONObject(); try { jo.put("text", annot.text); jo.put("uri", annot.url); - JSONObject userObject = new JSONObject(); - userObject.put("id", annot.creator); - - RestServer restServer = (RestServer) getApplication(); - - String userID = annot.creator; - if (userID.startsWith(NS.MPIWG_PERSONS)) { - userID = userID.replace(NS.MPIWG_PERSONS, ""); // entferne NAMESPACE + if (makeUserObject) { + // create user object + JSONObject userObject = new JSONObject(); + // save creator as uri + userObject.put("uri", annot.creator); + // make short user id + String userID = annot.creator; + if (userID.startsWith(NS.MPIWG_PERSONS)) { + userID = userID.replace(NS.MPIWG_PERSONS, ""); // entferne NAMESPACE + } + // save as id + userObject.put("id", userID); + // get full name + RestServer restServer = (RestServer) getApplication(); + String userName = restServer.getUserNameFromLdap(userID); + userObject.put("name", userName); + // save user object + jo.put("user", userObject); + } else { + // save user as string + jo.put("user", annot.creator); } - String userName = restServer.getUserNameFromLdap(userID); - userObject.put("name", userName); - jo.put("user", userObject); - - List<String> xpointer = new ArrayList<String>(); - + List<String> xpointers = new ArrayList<String>(); if (annot.xpointers == null || annot.xpointers.size() == 0) - xpointer.add(annot.xpointer); + xpointers.add(annot.xpointer); else { for (String xpointerString : annot.xpointers) { - xpointer.add(xpointerString); + xpointers.add(xpointerString); } } - jo.put("ranges", transformToRanges(xpointer)); + jo.put("ranges", transformToRanges(xpointers)); jo.put("id", annot.annotationUri); return jo; } catch (JSONException e) { @@ -235,56 +271,56 @@ } /** - * creates an Annotation object with data from JSON. - * uses the specification from the annotator project. + * creates an Annotation object with data from JSON. + * + * uses the specification from the annotator project: {@link https://github.com/okfn/annotator/wiki/Annotation-format} * - * @see{https://github.com/okfn/annotator/wiki/Annotation-format} + * The username will be transformed to an URI if not given already as URI, if not it will set to the MPIWG namespace defined in + * de.mpiwg.itgroup.annotationManager.Constants.NS * - * The username will be transformed to an URI if not given already - * as URI, if not it will set to the MPIWG namespace defined in - * de.mpiwg.itgroup.annotationManager.Constants.NS * @param jo * @return * @throws JSONException */ public Convert.Annotation createAnnotation(JSONObject jo, Representation entity) throws JSONException { - Convert.Annotation annot; String url = jo.getString("uri"); String text = jo.getString("text"); - - String username = null; - if (jo.has("user")) { - // not required, if no username given authuser - // will be used otherwise username and password - // has to be submitted - JSONObject user = jo.getJSONObject("user"); - if (user.has("id")) { - username = user.getString("id"); - if (!user.has("password")) { - User authUser = handleBasicAuthentification(entity); - if (authUser == null) { - setStatus(Status.CLIENT_ERROR_FORBIDDEN); - return null; - } - username = authUser.getIdentifier(); - } else { - String password = user.getString("password"); - if (!((RestServer) getApplication()).authenticate(username, password, getRequest())) { - setStatus(Status.CLIENT_ERROR_FORBIDDEN); - return null; - } - } - } - - } else { - User authUser = handleBasicAuthentification(entity); - if (authUser == null) { + + // check authentication + String authUser = checkAuthToken(entity); + if (authUser == null) { + // try http auth + User httpUser = getHttpAuthUser(entity); + if (httpUser == null) { setStatus(Status.CLIENT_ERROR_FORBIDDEN); return null; } - username = authUser.getIdentifier(); + authUser = httpUser.getIdentifier(); } - + // username not required, if no username given authuser will be used + String username = null; + String userUri = null; + if (jo.has("user")) { + if (jo.get("user") instanceof String) { + // user is just a String + username = jo.getString("user"); + // TODO: what if username and authUser are different? + } else { + // user is an object + JSONObject user = jo.getJSONObject("user"); + if (user.has("id")) { + username = user.getString("id"); + } + if (user.has("uri")) { + userUri = user.getString("uri"); + } + } + } + if (username == null) { + username = authUser; + } + + // create xpointer String xpointer; if (jo.has("ranges")) { JSONObject ranges = jo.getJSONArray("ranges").getJSONObject(0); @@ -292,7 +328,7 @@ String end = ranges.getString("end"); String startOffset = ranges.getString("startOffset"); String endOffset = ranges.getString("endOffset"); - + try { xpointer = url + "#" @@ -307,13 +343,36 @@ } else { xpointer = url; } - + // username should be a URI, if not it will set to the MPIWG namespace defined in // de.mpiwg.itgroup.annotationManager.Constants.NS - if (!username.startsWith("http")) - username = NS.MPIWG_PERSONS + username; - - return new Convert.Annotation(xpointer, username, null, text, null); + if (userUri == null) { + if (username.startsWith("http")) { + userUri = username; + } else { + userUri = NS.MPIWG_PERSONS + username; + } + } + return new Convert.Annotation(xpointer, userUri, null, text, null); + } + + /** + * returns the logged in User. + * + * @param entity + * @return + */ + protected User getHttpAuthUser(Representation entity) { + RestServer restServer = (RestServer) getApplication(); + if (!restServer.authenticate(getRequest(), getResponse())) { + // Not authenticated + return null; + } + + ClientInfo ci = getRequest().getClientInfo(); + logger.debug(ci); + return getRequest().getClientInfo().getUser(); + } }
--- a/src/de/mpiwg/itgroup/annotationManager/restlet/AnnotatorSearch.java Wed Mar 21 11:09:20 2012 +0100 +++ b/src/de/mpiwg/itgroup/annotationManager/restlet/AnnotatorSearch.java Wed Mar 21 15:23:57 2012 +0100 @@ -69,9 +69,9 @@ ja = new JSONArray(); for (Convert.Annotation annot : annots) { - JSONObject jo = annot2AnnotatorJSON(annot); + JSONObject jo = createAnnotatorJson(annot); if (jo != null) { - ja.put(annot2AnnotatorJSON(annot)); + ja.put(createAnnotatorJson(annot)); } else { setStatus(Status.SERVER_ERROR_INTERNAL, "JSON Error"); return null;
--- a/src/de/mpiwg/itgroup/annotationManager/restlet/RestServer.java Wed Mar 21 11:09:20 2012 +0100 +++ b/src/de/mpiwg/itgroup/annotationManager/restlet/RestServer.java Wed Mar 21 15:23:57 2012 +0100 @@ -186,8 +186,8 @@ Router router = new Router(getContext()); - router.attach("/annotations", AddAndReadAnnotations.class); - router.attach("/search", AnnotatorSearch.class); // annotator api askes + router.attach("/annotator/annotations", AnnotatorAnnotations.class); + router.attach("/annotator/search", AnnotatorSearch.class); // annotator api askes // for different uris // for search and // adding