source: DASISH/t5.6/backend/annotator-backend/trunk/annotator-backend/src/main/java/eu/dasish/annotation/backend/rest/AnnotationResource.java @ 4209

Last change on this file since 4209 was 4209, checked in by olhsha, 10 years ago

following Eric's instruction in security management in Dasish: removing a separate security DB, making authentication tables the part of the main DB, keeping hashed passwords. Used spring-security configuration for hashes with BCryptPasswordEncoder as recommended.

File size: 19.6 KB
Line 
1/*
2 * Copyright (C) 2013 DASISH
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
17 */
18package eu.dasish.annotation.backend.rest;
19
20import eu.dasish.annotation.backend.BackendConstants;
21import eu.dasish.annotation.backend.dao.DBIntegrityService;
22import eu.dasish.annotation.schema.Annotation;
23import eu.dasish.annotation.schema.AnnotationActionName;
24import eu.dasish.annotation.schema.AnnotationInfoList;
25import eu.dasish.annotation.schema.Action;
26import eu.dasish.annotation.schema.ActionList;
27import eu.dasish.annotation.schema.AnnotationBody;
28import eu.dasish.annotation.schema.ObjectFactory;
29import eu.dasish.annotation.schema.Permission;
30import eu.dasish.annotation.schema.PermissionActionName;
31import eu.dasish.annotation.schema.UserWithPermissionList;
32import eu.dasish.annotation.schema.ReferenceList;
33import eu.dasish.annotation.schema.ResponseBody;
34import java.io.IOException;
35import java.net.URI;
36import java.sql.Timestamp;
37import java.util.ArrayList;
38import java.util.List;
39import java.util.UUID;
40import javax.servlet.http.HttpServletRequest;
41import javax.servlet.http.HttpServletResponse;
42import javax.ws.rs.Consumes;
43import javax.ws.rs.DELETE;
44import javax.ws.rs.GET;
45import javax.ws.rs.POST;
46import javax.ws.rs.PUT;
47import javax.ws.rs.Path;
48import javax.ws.rs.PathParam;
49import javax.ws.rs.Produces;
50import javax.ws.rs.QueryParam;
51import javax.ws.rs.core.Context;
52import javax.ws.rs.core.MediaType;
53import javax.ws.rs.core.UriInfo;
54import javax.ws.rs.ext.Providers;
55import javax.xml.bind.JAXBElement;
56import org.springframework.beans.factory.annotation.Autowired;
57import org.springframework.security.access.annotation.Secured;
58import org.springframework.stereotype.Component;
59import org.slf4j.Logger;
60import org.slf4j.LoggerFactory;
61import org.springframework.transaction.annotation.Transactional;
62
63/**
64 *
65 * @author olhsha
66 */
67@Component
68@Path("/annotations")
69@Transactional(rollbackFor = {Exception.class})
70public class AnnotationResource {
71
72    @Autowired
73    private DBIntegrityService dbIntegrityService;
74    @Context
75    private HttpServletRequest httpServletRequest;
76    @Context
77    private HttpServletResponse httpServletResponse;
78    @Context
79    private UriInfo uriInfo;
80    @Context
81    private Providers providers;
82   
83   
84    final String default_permission = "reader";
85    private static final Logger logger = LoggerFactory.getLogger(AnnotationResource.class);
86
87    public void setUriInfo(UriInfo uriInfo) {
88        this.uriInfo = uriInfo;
89    }
90
91    public void setHttpServletResponse(HttpServletResponse httpServletResponse) {
92        this.httpServletResponse = httpServletResponse;
93    }
94
95    public void setHttpServletRequest(HttpServletRequest httpServletRequest) {
96        this.httpServletRequest = httpServletRequest;
97    }
98
99 
100   
101    public void setProviders(Providers providers) {
102        this.providers = providers;
103    }
104
105    public AnnotationResource() {
106    }
107
108    @GET
109    @Produces(MediaType.TEXT_XML)
110    @Path("{annotationid: " + BackendConstants.regExpIdentifier + "}")
111    @Secured("ROLE_USER")
112    @Transactional(readOnly = true)
113    public JAXBElement<Annotation> getAnnotation(@PathParam("annotationid") String ExternalIdentifier) throws IOException {
114        URI baseURI = uriInfo.getBaseUri();
115        String baseURIstr = baseURI.toString();
116        dbIntegrityService.setServiceURI(baseURIstr);
117        final Number annotationID = dbIntegrityService.getAnnotationInternalIdentifier(UUID.fromString(ExternalIdentifier));
118        String remoteUser = httpServletRequest.getRemoteUser();
119        final Number userID = dbIntegrityService.getUserInternalIDFromRemoteID(remoteUser);
120        if (canRead(userID, annotationID)) {
121            final Annotation annotation = dbIntegrityService.getAnnotation(annotationID);
122            JAXBElement<Annotation> rootElement = new ObjectFactory().createAnnotation(annotation);
123            logger.info("getAnnotation method: OK");
124            return rootElement;
125        } else {
126            httpServletResponse.sendError(HttpServletResponse.SC_FORBIDDEN, "The logged-in user cannot read the annotation.");
127            return null;
128        }
129
130    }
131
132    //TODO: unit test
133    @GET
134    @Produces(MediaType.TEXT_XML)
135    @Path("{annotationid: " + BackendConstants.regExpIdentifier + "}/targets")
136    @Secured("ROLE_USER")
137    @Transactional(readOnly = true)
138    public JAXBElement<ReferenceList> getAnnotationTargets(@PathParam("annotationid") String ExternalIdentifier) throws IOException {
139        dbIntegrityService.setServiceURI(uriInfo.getBaseUri().toString());
140        final Number annotationID = dbIntegrityService.getAnnotationInternalIdentifier(UUID.fromString(ExternalIdentifier));
141        final Number userID = dbIntegrityService.getUserInternalIDFromRemoteID(httpServletRequest.getRemoteUser());
142        if (canRead(userID, annotationID)) {
143            final ReferenceList TargetList = dbIntegrityService.getAnnotationTargets(annotationID);
144            logger.info("getAnnotationTargets method: OK");
145            return new ObjectFactory().createTargetList(TargetList);
146        } else {
147            httpServletResponse.sendError(HttpServletResponse.SC_FORBIDDEN, "The logged-in user cannot read the annotation.");
148            return null;
149        }
150    }
151
152    // TODO Unit test
153    @GET
154    @Produces(MediaType.TEXT_XML)
155    @Path("")
156    @Secured("ROLE_USER")
157    @Transactional(readOnly = true)
158    public JAXBElement<AnnotationInfoList> getFilteredAnnotations(@QueryParam("link") String link,
159            @QueryParam("text") String text,
160            @QueryParam("access") String permission,
161            @QueryParam("namespace") String namespace,
162            @QueryParam("owner") String ownerExternalId,
163            @QueryParam("after") Timestamp after,
164            @QueryParam("before") Timestamp before) {
165
166        dbIntegrityService.setServiceURI(uriInfo.getBaseUri().toString());
167        Number userID = dbIntegrityService.getUserInternalIDFromRemoteID(httpServletRequest.getRemoteUser());
168        UUID ownerExternalUUID = (ownerExternalId != null) ? UUID.fromString(ownerExternalId) : null;
169        String access = (permission != null) ? permission : default_permission;
170        final AnnotationInfoList annotationInfoList = dbIntegrityService.getFilteredAnnotationInfos(link, text, userID, makeAccessModeChain(access), namespace, ownerExternalUUID, after, before);
171        logger.info("getFilteredAnnotations method: OK");
172        return new ObjectFactory().createAnnotationInfoList(annotationInfoList);
173    }
174
175    // TODO Unit test   
176    @GET
177    @Produces(MediaType.TEXT_XML)
178    @Path("{annotationid: " + BackendConstants.regExpIdentifier + "}/permissions")
179    @Secured("ROLE_USER")
180    @Transactional(readOnly = true)
181    public JAXBElement<UserWithPermissionList> getAnnotationPermissions(@PathParam("annotationid") String ExternalIdentifier) throws IOException {
182        dbIntegrityService.setServiceURI(uriInfo.getBaseUri().toString());
183        final Number annotationID = dbIntegrityService.getAnnotationInternalIdentifier(UUID.fromString(ExternalIdentifier));
184        final Number userID = dbIntegrityService.getUserInternalIDFromRemoteID(httpServletRequest.getRemoteUser());
185        if (canRead(userID, annotationID)) {
186            final UserWithPermissionList permissionList = dbIntegrityService.getPermissionsForAnnotation(annotationID);
187            logger.info("getAnnotationPermissions method: OK");
188            return new ObjectFactory().createPermissionList(permissionList);
189        } else {
190            httpServletResponse.sendError(HttpServletResponse.SC_FORBIDDEN, "The logged-in user cannot read the annotation.");
191            return null;
192        }
193
194    }
195
196    ///////////////////////////////////////////////////////
197    // TODO: how to return the status code?
198    @DELETE
199    @Path("{annotationid: " + BackendConstants.regExpIdentifier + "}")
200    @Secured("ROLE_USER")
201    public String deleteAnnotation(@PathParam("annotationid") String externalIdentifier) throws IOException {
202        dbIntegrityService.setServiceURI(uriInfo.getBaseUri().toString());
203        final Number annotationID = dbIntegrityService.getAnnotationInternalIdentifier(UUID.fromString(externalIdentifier));
204        final Number userID = dbIntegrityService.getUserInternalIDFromRemoteID(httpServletRequest.getRemoteUser());
205        if (isOwner(userID, annotationID)) {
206            int[] resultDelete = dbIntegrityService.deleteAnnotation(annotationID);
207            String result = Integer.toString(resultDelete[0]);
208            logger.info("deleteAnnotation method: OK");
209            return result + " annotation(s) deleted.";
210        } else {
211            httpServletResponse.sendError(HttpServletResponse.SC_FORBIDDEN, "The logged-in user cannot delete the annotation. Only the owner can delete the annotation.");
212            return null;
213        }
214
215    }
216
217    ///////////////////////////////////////////////////////
218    @POST
219    @Consumes(MediaType.APPLICATION_XML)
220    @Produces(MediaType.APPLICATION_XML)
221    @Path("")
222    @Secured("ROLE_USER")
223    public JAXBElement<ResponseBody> createAnnotation(Annotation annotation) throws IOException {
224        dbIntegrityService.setServiceURI(uriInfo.getBaseUri().toString());
225        final Number userID = dbIntegrityService.getUserInternalIDFromRemoteID(httpServletRequest.getRemoteUser());
226        Number annotationID = dbIntegrityService.addUsersAnnotation(userID, annotation);
227        logger.info("createAnnotation method: OK");
228        return new ObjectFactory().createResponseBody(makeAnnotationResponseEnvelope(annotationID));
229    }
230
231    ///////////////////////////////////////////////////////
232    // TODO: unit test
233    @PUT
234    @Consumes(MediaType.APPLICATION_XML)
235    @Produces(MediaType.APPLICATION_XML)
236    @Path("{annotationid: " + BackendConstants.regExpIdentifier + "}")
237    @Secured("ROLE_USER")
238    public JAXBElement<ResponseBody> updateAnnotation(@PathParam("annotationid") String externalIdentifier, Annotation annotation) throws IOException {
239        String path = uriInfo.getBaseUri().toString();
240        dbIntegrityService.setServiceURI(path);
241        String annotationURI = annotation.getURI();
242        if (!(path + "annotations/" + externalIdentifier).equals(annotationURI)) {
243            logger.error("Wrong request: the external annotation ID and the annotation ID from the request body do not match.");
244            logger.error("Will do nothing.");
245            return null;
246        }
247        final Number annotationID = dbIntegrityService.getAnnotationInternalIdentifier(UUID.fromString(externalIdentifier));
248        final Number userID = dbIntegrityService.getUserInternalIDFromRemoteID(httpServletRequest.getRemoteUser());
249        if (canWrite(userID, annotationID)) {
250            int updatedRows = dbIntegrityService.updateUsersAnnotation(userID, annotation);
251            logger.info("updateAnnotation method: OK");
252            return new ObjectFactory().createResponseBody(makeAnnotationResponseEnvelope(annotationID));
253
254        } else {
255            httpServletResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);
256            return null;
257        }
258
259    }
260
261    @PUT
262    @Consumes(MediaType.APPLICATION_XML)
263    @Produces(MediaType.APPLICATION_XML)
264    @Path("{annotationid: " + BackendConstants.regExpIdentifier + "}/body")
265    @Secured("ROLE_USER")
266    public JAXBElement<ResponseBody> updateAnnotationBody(@PathParam("annotationid") String externalIdentifier, AnnotationBody annotationBody) throws IOException {
267        String path = uriInfo.getBaseUri().toString();
268        dbIntegrityService.setServiceURI(path);
269
270        final Number annotationID = dbIntegrityService.getAnnotationInternalIdentifier(UUID.fromString(externalIdentifier));
271        final Number userID = dbIntegrityService.getUserInternalIDFromRemoteID(httpServletRequest.getRemoteUser());
272        if (canWrite(userID, annotationID)) {
273            int updatedRows = dbIntegrityService.updateAnnotationBody(annotationID, annotationBody);
274            logger.info("updateAnnotationBody method: OK");
275            return new ObjectFactory().createResponseBody(makeAnnotationResponseEnvelope(annotationID));
276        } else {
277            httpServletResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);
278            return null;
279        }
280
281    }
282
283    @PUT
284    @Consumes(MediaType.APPLICATION_XML)
285    @Produces(MediaType.APPLICATION_XML)
286    @Path("{annotationid: " + BackendConstants.regExpIdentifier + "}/permissions/{userid: " + BackendConstants.regExpIdentifier + "}")
287    @Secured("ROLE_USER")
288    public String updatePermission(@PathParam("annotationid") String annotationExternalId, @PathParam("userid") String userExternalId, Permission permission) throws IOException {
289        dbIntegrityService.setServiceURI(uriInfo.getBaseUri().toString());
290        final Number annotationID = dbIntegrityService.getAnnotationInternalIdentifier(UUID.fromString(annotationExternalId));
291        final Number remoteUserID = dbIntegrityService.getUserInternalIDFromRemoteID(httpServletRequest.getRemoteUser());
292        final Number userID = dbIntegrityService.getUserInternalIdentifier(UUID.fromString(userExternalId));
293        if (isOwner(remoteUserID, annotationID)) {
294            int result = (dbIntegrityService.getPermission(annotationID, userID) != null)
295                    ? dbIntegrityService.updateAnnotationPrincipalPermission(annotationID, userID, permission)
296                    : dbIntegrityService.addAnnotationPrincipalPermission(annotationID, userID, permission);
297            logger.info("updatePermission method: OK");
298            return result + " rows are updated/added";
299
300        } else {
301            httpServletResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);
302            return null;
303        }
304    }
305
306    @PUT
307    @Consumes(MediaType.APPLICATION_XML)
308    @Produces(MediaType.APPLICATION_XML)
309    @Path("{annotationid: " + BackendConstants.regExpIdentifier + "}/permissions/")
310    @Secured("ROLE_USER")
311    public JAXBElement<ResponseBody> updatePermissions(@PathParam("annotationid") String annotationExternalId, UserWithPermissionList permissions) throws IOException {
312        dbIntegrityService.setServiceURI(uriInfo.getBaseUri().toString());
313        final Number annotationID = dbIntegrityService.getAnnotationInternalIdentifier(UUID.fromString(annotationExternalId));
314        final Number remoteUserID = dbIntegrityService.getUserInternalIDFromRemoteID(httpServletRequest.getRemoteUser());
315        if (isOwner(remoteUserID, annotationID)) {
316            int updatedRows = dbIntegrityService.updatePermissions(annotationID, permissions);
317            logger.info("updatePermissions method: OK");
318            return new ObjectFactory().createResponseBody(makePermissionResponseEnvelope(annotationID));
319        } else {
320            httpServletResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);
321            return null;
322        }
323    }
324
325    /////////////////////////////////////////
326    private ResponseBody makeAnnotationResponseEnvelope(Number annotationID) {
327        ResponseBody result = new ResponseBody();
328        result.setPermissions(null);
329        Annotation annotation = dbIntegrityService.getAnnotation(annotationID);
330        result.setAnnotation(annotation);
331        List<String> targetsNoCached = dbIntegrityService.getTargetsWithNoCachedRepresentation(annotationID);
332        ActionList actionList = new ActionList();
333        result.setActionList(actionList);
334        actionList.getAction().addAll(makeActionList(targetsNoCached, AnnotationActionName.CREATE_CACHED_REPRESENTATION.value()));
335        return result;
336    }
337
338    /////////////////////////////////////////
339    private ResponseBody makePermissionResponseEnvelope(Number annotationID) {
340        ResponseBody result = new ResponseBody();
341        result.setAnnotation(null);
342        UserWithPermissionList permissions = dbIntegrityService.getPermissionsForAnnotation(annotationID);
343        result.setPermissions(permissions);
344        List<String> usersWithNoInfo = dbIntegrityService.getUsersWithNoInfo(annotationID);
345        ActionList actionList = new ActionList();
346        result.setActionList(actionList);
347        actionList.getAction().addAll(makeActionList(usersWithNoInfo, PermissionActionName.PROVIDE_USER_INFO.value()));
348        return result;
349    }
350
351    // REFACTOR : move to the integrity service all te methods below 
352    private List<Action> makeActionList(List<String> resourceURIs, String message) {
353        if (resourceURIs != null) {
354            if (resourceURIs.isEmpty()) {
355                return (new ArrayList<Action>());
356            } else {
357                List<Action> result = new ArrayList<Action>();
358                for (String resourceURI : resourceURIs) {
359                    Action action = new Action();
360                    result.add(action);
361                    action.setMessage(message);
362                    action.setObject(resourceURI);
363                }
364                return result;
365            }
366        } else {
367            return null;
368        }
369    }
370
371    private boolean canRead(Number userID, Number annotationID) {
372        final Permission permission = dbIntegrityService.getPermission(annotationID, userID);
373        if (permission != null) {
374            return (permission.value() == Permission.OWNER.value() || permission.value() == Permission.WRITER.value() || permission.value() == Permission.READER.value());
375        } else {
376            return false;
377        }
378    }
379
380    private boolean canWrite(Number userID, Number annotationID) {
381        final Permission permission = dbIntegrityService.getPermission(annotationID, userID);
382        if (permission != null) {
383            return (permission.value() == Permission.OWNER.value() || permission.value() == Permission.WRITER.value());
384        } else {
385            return false;
386        }
387    }
388
389    private boolean isOwner(Number userID, Number annotationID) {
390        final Permission permission = dbIntegrityService.getPermission(annotationID, userID);
391        if (permission != null) {
392            return (permission.value() == Permission.OWNER.value());
393        } else {
394            return false;
395        }
396    }
397
398    private String[] makeAccessModeChain(String accessMode) {
399        if (accessMode != null) {
400            if (accessMode == Permission.OWNER.value()) {
401                String[] result = new String[1];
402                result[0] = accessMode;
403                return result;
404            } else {
405                if (accessMode == Permission.WRITER.value()) {
406                    String[] result = new String[2];
407                    result[0] = Permission.WRITER.value();
408                    result[1] = Permission.OWNER.value();
409                    return result;
410                } else {
411                    if (accessMode == Permission.READER.value()) {
412                        String[] result = new String[3];
413                        result[0] = Permission.READER.value();
414                        result[1] = Permission.WRITER.value();
415                        result[2] = Permission.OWNER.value();
416                        return result;
417                    } else {
418                        logger.error("Invalide access " + accessMode);
419                        return null;
420                    }
421
422                }
423            }
424
425        } else {
426            return null;
427        }
428    }
429}
Note: See TracBrowser for help on using the repository browser.