8
|
1 /**
|
|
2 * Base class for Annotator resource classes.
|
|
3 */
|
|
4 package de.mpiwg.itgroup.annotationManager.restlet;
|
|
5
|
|
6 import java.io.UnsupportedEncodingException;
|
|
7 import java.net.URLDecoder;
|
10
|
8 import java.security.MessageDigest;
|
|
9 import java.security.NoSuchAlgorithmException;
|
8
|
10 import java.util.ArrayList;
|
|
11 import java.util.List;
|
|
12 import java.util.regex.Matcher;
|
|
13 import java.util.regex.Pattern;
|
|
14
|
10
|
15 import org.apache.log4j.Logger;
|
|
16 import org.joda.time.DateTime;
|
|
17 import org.joda.time.format.DateTimeFormatter;
|
|
18 import org.joda.time.format.ISODateTimeFormat;
|
8
|
19 import org.json.JSONArray;
|
|
20 import org.json.JSONException;
|
|
21 import org.json.JSONObject;
|
|
22 import org.restlet.data.Form;
|
|
23 import org.restlet.representation.Representation;
|
|
24 import org.restlet.resource.Options;
|
|
25 import org.restlet.resource.ServerResource;
|
|
26
|
|
27 import de.mpiwg.itgroup.annotationManager.Constants.NS;
|
|
28 import de.mpiwg.itgroup.annotationManager.RDFHandling.Convert;
|
|
29
|
|
30 /**
|
|
31 * Base class for Annotator resource classes.
|
|
32 *
|
|
33 * @author dwinter, casties
|
|
34 *
|
|
35 */
|
|
36 public abstract class AnnotatorResourceImpl extends ServerResource {
|
|
37
|
10
|
38 protected Logger logger = Logger.getRootLogger();
|
|
39
|
8
|
40 protected String getAllowedMethodsForHeader() {
|
|
41 return "OPTIONS,GET,POST";
|
|
42 }
|
|
43
|
|
44 /**
|
|
45 * Handle options request to allow CORS for AJAX.
|
|
46 *
|
|
47 * @param entity
|
|
48 */
|
|
49 @Options
|
|
50 public void doOptions(Representation entity) {
|
10
|
51 Form responseHeaders = (Form) getResponse().getAttributes().get("org.restlet.http.headers");
|
8
|
52 if (responseHeaders == null) {
|
|
53 responseHeaders = new Form();
|
10
|
54 getResponse().getAttributes().put("org.restlet.http.headers", responseHeaders);
|
8
|
55 }
|
10
|
56 responseHeaders.add("Access-Control-Allow-Methods", getAllowedMethodsForHeader());
|
8
|
57 // echo back Origin and Request-Headers
|
10
|
58 Form requestHeaders = (Form) getRequest().getAttributes().get("org.restlet.http.headers");
|
8
|
59 String origin = requestHeaders.getFirstValue("Origin", true);
|
|
60 if (origin == null) {
|
|
61 responseHeaders.add("Access-Control-Allow-Origin", "*");
|
|
62 } else {
|
|
63 responseHeaders.add("Access-Control-Allow-Origin", origin);
|
|
64 }
|
10
|
65 String allowHeaders = requestHeaders.getFirstValue("Access-Control-Request-Headers", true);
|
8
|
66 if (allowHeaders != null) {
|
|
67 responseHeaders.add("Access-Control-Allow-Headers", allowHeaders);
|
|
68 }
|
|
69 responseHeaders.add("Access-Control-Allow-Credentials", "true");
|
|
70 responseHeaders.add("Access-Control-Max-Age", "60");
|
|
71 }
|
|
72
|
|
73 /**
|
10
|
74 * returns if authentication information from headers is valid.
|
|
75 *
|
|
76 * @param entity
|
|
77 * @return
|
|
78 */
|
|
79 public boolean isAuthenticated(Representation entity) {
|
|
80 // get authToken
|
|
81 Form requestHeaders = (Form) getRequest().getAttributes().get("org.restlet.http.headers");
|
|
82 String consumerKey = requestHeaders.getFirstValue("x-annotator-consumer-key", true);
|
|
83 if (consumerKey == null) {
|
|
84 return false;
|
|
85 }
|
|
86 RestServer restServer = (RestServer) getApplication();
|
|
87 String consumerSecret = restServer.getConsumerSecret(consumerKey);
|
|
88 logger.debug("requested consumer key=" + consumerKey + " secret=" + consumerSecret);
|
|
89 if (consumerSecret == null) {
|
|
90 return false;
|
|
91 }
|
|
92 String userId = requestHeaders.getFirstValue("x-annotator-user-id", true);
|
|
93 String issueTime = requestHeaders.getFirstValue("x-annotator-auth-token-issue-time", true);
|
|
94 if (userId == null || issueTime == null) {
|
|
95 return false;
|
|
96 }
|
|
97 // compute hashed token based on the values we know
|
|
98 // computed_token = hashlib.sha256(consumer.secret + user_id + issue_time).hexdigest()
|
|
99 String computedToken;
|
|
100 try {
|
|
101 MessageDigest md = MessageDigest.getInstance("SHA-256");
|
|
102 String computedString = consumerSecret + userId + issueTime;
|
|
103 md.update(computedString.getBytes("UTF-8"));
|
|
104 byte[] dg = md.digest();
|
|
105 StringBuffer sb = new StringBuffer();
|
|
106 for (byte b : dg) {
|
|
107 sb.append(String.format("%02x", b));
|
|
108 }
|
|
109 computedToken = sb.toString();
|
|
110 } catch (NoSuchAlgorithmException e) {
|
|
111 e.printStackTrace();
|
|
112 return false;
|
|
113 } catch (UnsupportedEncodingException e) {
|
|
114 e.printStackTrace();
|
|
115 return false;
|
|
116 }
|
|
117 // compare to the token we got
|
|
118 String authToken = requestHeaders.getFirstValue("x-annotator-auth-token", true);
|
|
119 logger.debug(String.format("got: authToken=%s consumerSecret=%s userId=%s issueTime=%s", authToken, consumerSecret, userId,
|
|
120 issueTime));
|
|
121 if (!computedToken.equals(authToken)) {
|
|
122 return false;
|
|
123 }
|
|
124 // check token lifetime
|
|
125 // validity = iso8601.parse_date(issue_time)
|
|
126 // expiry = validity + datetime.timedelta(seconds=consumer.ttl)
|
|
127 int tokenTtl = 86400;
|
|
128 DateTime tokenValidity = null;
|
|
129 DateTime tokenExpiry = null;
|
|
130 try {
|
|
131 DateTimeFormatter parser = ISODateTimeFormat.dateTime();
|
|
132 tokenValidity = parser.parseDateTime(issueTime);
|
|
133 String tokenTtlString = requestHeaders.getFirstValue("x-annotator-auth-token-ttl", true);
|
|
134 tokenTtl = Integer.parseInt(tokenTtlString);
|
|
135 tokenExpiry = tokenValidity.plusSeconds(tokenTtl);
|
|
136 } catch (NumberFormatException e) {
|
|
137 e.printStackTrace();
|
|
138 }
|
|
139 if (tokenValidity == null || tokenValidity.isAfterNow() || tokenExpiry.isBeforeNow()) {
|
|
140 return false;
|
|
141 }
|
|
142 // must be ok then
|
|
143 return true;
|
|
144 }
|
|
145
|
|
146 /**
|
|
147 * Erzeugt aus einer Annotation, das fuer den Annotator notwendige JSON-Format
|
8
|
148 *
|
|
149 * @param annot
|
|
150 * @return
|
|
151 */
|
|
152 protected JSONObject annot2AnnotatorJSON(Convert.Annotation annot) {
|
|
153 JSONObject jo = new JSONObject();
|
|
154 try {
|
|
155 jo.put("text", annot.text);
|
|
156 jo.put("uri", annot.url);
|
|
157
|
|
158 JSONObject userObject = new JSONObject();
|
|
159 userObject.put("id", annot.creator);
|
|
160
|
|
161 RestServer restServer = (RestServer) getApplication();
|
|
162
|
|
163 String userID = annot.creator;
|
|
164 if (userID.startsWith(NS.MPIWG_PERSONS)) {
|
10
|
165 userID = userID.replace(NS.MPIWG_PERSONS, ""); // entferne NAMESPACE
|
8
|
166 }
|
|
167 String userName = restServer.getUserNameFromLdap(userID);
|
|
168 userObject.put("name", userName);
|
|
169
|
|
170 jo.put("user", userObject);
|
|
171
|
|
172 List<String> xpointer = new ArrayList<String>();
|
|
173
|
|
174 if (annot.xpointers == null || annot.xpointers.size() == 0)
|
|
175 xpointer.add(annot.xpointer);
|
|
176 else {
|
|
177 for (String xpointerString : annot.xpointers) {
|
|
178 xpointer.add(xpointerString);
|
|
179 }
|
|
180 }
|
|
181 jo.put("ranges", transformToRanges(xpointer));
|
|
182 jo.put("id", annot.annotationUri);
|
|
183 return jo;
|
|
184 } catch (JSONException e) {
|
|
185 // TODO Auto-generated catch block
|
|
186 e.printStackTrace();
|
|
187 return null;
|
|
188 }
|
|
189 }
|
|
190
|
|
191 private JSONArray transformToRanges(List<String> xpointers) {
|
|
192
|
|
193 JSONArray ja = new JSONArray();
|
|
194
|
|
195 Pattern rg = Pattern
|
|
196 .compile("#xpointer\\(start-point\\(string-range\\(\"([^\"]*)\",([^,]*),1\\)\\)/range-to\\(end-point\\(string-range\\(\"([^\"]*)\",([^,]*),1\\)\\)\\)\\)");
|
10
|
197 Pattern rg1 = Pattern.compile("#xpointer\\(start-point\\(string-range\\(\"([^\"]*)\",([^,]*),1\\)\\)\\)");
|
8
|
198
|
|
199 try {
|
|
200 for (String xpointer : xpointers) {
|
|
201 String decoded = URLDecoder.decode(xpointer, "utf-8");
|
|
202 Matcher m = rg.matcher(decoded);
|
|
203
|
|
204 if (m.find()) {
|
|
205 {
|
|
206 JSONObject jo = new JSONObject();
|
|
207 jo.put("start", m.group(1));
|
|
208 jo.put("startOffset", m.group(2));
|
|
209 jo.put("end", m.group(3));
|
|
210 jo.put("endOffset", m.group(4));
|
|
211 ja.put(jo);
|
|
212 }
|
|
213 }
|
|
214 m = rg1.matcher(xpointer);
|
|
215 if (m.find()) {
|
|
216 JSONObject jo = new JSONObject();
|
|
217 jo.put("start", m.group(1));
|
|
218 jo.put("startOffset", m.group(2));
|
|
219
|
|
220 ja.put(jo);
|
|
221 }
|
|
222 }
|
|
223 } catch (JSONException e) {
|
|
224 // TODO Auto-generated catch block
|
|
225 e.printStackTrace();
|
|
226 } catch (UnsupportedEncodingException e) {
|
|
227 // TODO Auto-generated catch block
|
|
228 e.printStackTrace();
|
|
229 }
|
|
230
|
|
231 return ja;
|
|
232 }
|
|
233
|
|
234 }
|