#32: Comments system (server side)

This commit is contained in:
jendib 2015-11-16 02:22:51 +01:00
parent b3e44b84d2
commit 97252bb5da
30 changed files with 743 additions and 124 deletions

View File

@ -67,6 +67,7 @@ public class AuditLogDao {
// ACL on document is not checked here, it's assumed // ACL on document is not checked here, it's assumed
queries.add(baseQuery + " where l.LOG_IDENTITY_C = :documentId "); queries.add(baseQuery + " where l.LOG_IDENTITY_C = :documentId ");
queries.add(baseQuery + " where l.LOG_IDENTITY_C in (select f.FIL_ID_C from T_FILE f where f.FIL_IDDOC_C = :documentId) "); queries.add(baseQuery + " where l.LOG_IDENTITY_C in (select f.FIL_ID_C from T_FILE f where f.FIL_IDDOC_C = :documentId) ");
queries.add(baseQuery + " where l.LOG_IDENTITY_C in (select c.COM_ID_C from T_COMMENT c where c.COM_IDDOC_C = :documentId) ");
queries.add(baseQuery + " where l.LOG_IDENTITY_C in (select a.ACL_ID_C from T_ACL a where a.ACL_SOURCEID_C = :documentId) "); queries.add(baseQuery + " where l.LOG_IDENTITY_C in (select a.ACL_ID_C from T_ACL a where a.ACL_SOURCEID_C = :documentId) ");
parameterMap.put("documentId", criteria.getDocumentId()); parameterMap.put("documentId", criteria.getDocumentId());
} }
@ -76,6 +77,7 @@ public class AuditLogDao {
queries.add(baseQuery + " where l.LOG_IDENTITY_C in (select t.TAG_ID_C from T_TAG t where t.TAG_IDUSER_C = :userId) "); queries.add(baseQuery + " where l.LOG_IDENTITY_C in (select t.TAG_ID_C from T_TAG t where t.TAG_IDUSER_C = :userId) ");
// Show only logs from owned documents, ACL are lost on delete // Show only logs from owned documents, ACL are lost on delete
queries.add(baseQuery + " where l.LOG_IDENTITY_C in (select d.DOC_ID_C from T_DOCUMENT d where d.DOC_IDUSER_C = :userId) "); queries.add(baseQuery + " where l.LOG_IDENTITY_C in (select d.DOC_ID_C from T_DOCUMENT d where d.DOC_IDUSER_C = :userId) ");
queries.add(baseQuery + " where l.LOG_IDENTITY_C in (select c.COM_ID_C from T_COMMENT c where c.COM_IDUSER_C = :userId) ");
parameterMap.put("userId", criteria.getUserId()); parameterMap.put("userId", criteria.getUserId());
} }

View File

@ -0,0 +1,113 @@
package com.sismics.docs.core.dao.jpa;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.Query;
import com.sismics.docs.core.constant.AuditLogType;
import com.sismics.docs.core.dao.jpa.dto.CommentDto;
import com.sismics.docs.core.model.jpa.Comment;
import com.sismics.docs.core.util.AuditLogUtil;
import com.sismics.util.context.ThreadLocalContext;
/**
* Comment DAO.
*
* @author bgamard
*/
public class CommentDao {
/**
* Creates a new comment.
*
* @param comment Comment
* @return New ID
* @throws Exception
*/
public String create(Comment comment) {
// Create the UUID
comment.setId(UUID.randomUUID().toString());
// Create the comment
EntityManager em = ThreadLocalContext.get().getEntityManager();
comment.setCreateDate(new Date());
em.persist(comment);
// Create audit log
AuditLogUtil.create(comment, AuditLogType.CREATE);
return comment.getId();
}
/**
* Deletes a comment.
*
* @param id Comment ID
*/
public void delete(String id) {
EntityManager em = ThreadLocalContext.get().getEntityManager();
// Get the document
Query q = em.createQuery("select c from Comment c where c.id = :id and c.deleteDate is null");
q.setParameter("id", id);
Comment commentDb = (Comment) q.getSingleResult();
// Delete the document
Date dateNow = new Date();
commentDb.setDeleteDate(dateNow);
// Create audit log
AuditLogUtil.create(commentDb, AuditLogType.DELETE);
}
/**
* Gets an active comment by its ID.
*
* @param id Comment ID
* @return Comment
*/
public Comment getActiveById(String id) {
EntityManager em = ThreadLocalContext.get().getEntityManager();
try {
Query q = em.createQuery("select c from Comment c where c.id = :id and c.deleteDate is null");
q.setParameter("id", id);
return (Comment) q.getSingleResult();
} catch (NoResultException e) {
return null;
}
}
/**
* Get all comments on a document.
*
* @param documentId Document ID
* @return List of comments
*/
public List<CommentDto> getByDocumentId(String documentId) {
EntityManager em = ThreadLocalContext.get().getEntityManager();
StringBuilder sb = new StringBuilder("select c.COM_ID_C, c.COM_CONTENT_C, c.COM_CREATEDATE_D, u.USE_USERNAME_C from T_COMMENT c, T_USER u");
sb.append(" where c.COM_IDDOC_C = :documentId and c.COM_IDUSER_C = u.USE_ID_C and c.COM_DELETEDATE_D is null ");
sb.append(" order by c.COM_CREATEDATE_D asc ");
Query q = em.createNativeQuery(sb.toString());
q.setParameter("documentId", documentId);
@SuppressWarnings("unchecked")
List<Object[]> l = q.getResultList();
List<CommentDto> commentDtoList = new ArrayList<CommentDto>();
for (Object[] o : l) {
int i = 0;
CommentDto commentDto = new CommentDto();
commentDto.setId((String) o[i++]);
commentDto.setContent((String) o[i++]);
commentDto.setCreateTimestamp(((Timestamp) o[i++]).getTime());
commentDto.setCreatorName((String) o[i++]);
commentDtoList.add(commentDto);
}
return commentDtoList;
}
}

View File

@ -83,7 +83,12 @@ public class DocumentDao {
sb.append(" where d.DOC_IDUSER_C = u.USE_ID_C and d.DOC_ID_C = :id and d.DOC_DELETEDATE_D is null "); sb.append(" where d.DOC_IDUSER_C = u.USE_ID_C and d.DOC_ID_C = :id and d.DOC_DELETEDATE_D is null ");
Query q = em.createNativeQuery(sb.toString()); Query q = em.createNativeQuery(sb.toString());
q.setParameter("id", id); q.setParameter("id", id);
Object[] o = (Object[]) q.getSingleResult(); Object[] o = null;
try {
o = (Object[]) q.getSingleResult();
} catch (NoResultException e) {
return null;
}
DocumentDto documentDto = new DocumentDto(); DocumentDto documentDto = new DocumentDto();
int i = 0; int i = 0;
@ -114,7 +119,11 @@ public class DocumentDao {
q.setParameter("id", id); q.setParameter("id", id);
q.setParameter("perm", perm.name()); q.setParameter("perm", perm.name());
q.setParameter("userId", userId); q.setParameter("userId", userId);
return (Document) q.getSingleResult(); try {
return (Document) q.getSingleResult();
} catch (NoResultException e) {
return null;
}
} }
/** /**

View File

@ -63,7 +63,11 @@ public class FileDao {
EntityManager em = ThreadLocalContext.get().getEntityManager(); EntityManager em = ThreadLocalContext.get().getEntityManager();
Query q = em.createQuery("select f from File f where f.id = :id and f.deleteDate is null"); Query q = em.createQuery("select f from File f where f.id = :id and f.deleteDate is null");
q.setParameter("id", id); q.setParameter("id", id);
return (File) q.getSingleResult(); try {
return (File) q.getSingleResult();
} catch (NoResultException e) {
return null;
}
} }
/** /**
@ -78,7 +82,11 @@ public class FileDao {
Query q = em.createQuery("select f from File f where f.id = :id and f.userId = :userId and f.deleteDate is null"); Query q = em.createQuery("select f from File f where f.id = :id and f.userId = :userId and f.deleteDate is null");
q.setParameter("id", id); q.setParameter("id", id);
q.setParameter("userId", userId); q.setParameter("userId", userId);
return (File) q.getSingleResult(); try {
return (File) q.getSingleResult();
} catch (NoResultException e) {
return null;
}
} }
/** /**

View File

@ -0,0 +1,63 @@
package com.sismics.docs.core.dao.jpa.dto;
import javax.persistence.Id;
/**
* Comment DTO.
*
* @author bgamard
*/
public class CommentDto {
/**
* Comment ID.
*/
@Id
private String id;
/**
* Creator name.
*/
private String creatorName;
/**
* Content.
*/
private String content;
/**
* Creation date of this comment.
*/
private Long createTimestamp;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getCreatorName() {
return creatorName;
}
public void setCreatorName(String creatorName) {
this.creatorName = creatorName;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public Long getCreateTimestamp() {
return createTimestamp;
}
public void setCreateTimestamp(Long createTimestamp) {
this.createTimestamp = createTimestamp;
}
}

View File

@ -4,7 +4,6 @@ import java.util.Date;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.EntityListeners;
import javax.persistence.EnumType; import javax.persistence.EnumType;
import javax.persistence.Enumerated; import javax.persistence.Enumerated;
import javax.persistence.Id; import javax.persistence.Id;
@ -12,7 +11,6 @@ import javax.persistence.Table;
import com.google.common.base.MoreObjects; import com.google.common.base.MoreObjects;
import com.sismics.docs.core.constant.PermType; import com.sismics.docs.core.constant.PermType;
import com.sismics.docs.core.util.AuditLogUtil;
/** /**
* ACL entity. * ACL entity.
@ -20,7 +18,6 @@ import com.sismics.docs.core.util.AuditLogUtil;
* @author bgamard * @author bgamard
*/ */
@Entity @Entity
@EntityListeners(AuditLogUtil.class)
@Table(name = "T_ACL") @Table(name = "T_ACL")
public class Acl implements Loggable { public class Acl implements Loggable {
/** /**

View File

@ -0,0 +1,179 @@
package com.sismics.docs.core.model.jpa;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import com.google.common.base.MoreObjects;
/**
* Comment entity.
*
* @author bgamard
*/
@Entity
@Table(name = "T_COMMENT")
public class Comment implements Loggable {
/**
* Comment ID.
*/
@Id
@Column(name = "COM_ID_C", length = 36)
private String id;
/**
* Document ID.
*/
@Column(name = "COM_IDDOC_C", length = 36, nullable = false)
private String documentId;
/**
* User ID.
*/
@Column(name = "COM_IDUSER_C", length = 36, nullable = false)
private String userId;
/**
* Content.
*/
@Column(name = "COM_CONTENT_C", nullable = false)
private String content;
/**
* Creation date.
*/
@Column(name = "COM_CREATEDATE_D", nullable = false)
private Date createDate;
/**
* Deletion date.
*/
@Column(name = "COM_DELETEDATE_D")
private Date deleteDate;
/**
* Getter of id.
*
* @return the id
*/
public String getId() {
return id;
}
/**
* Setter of id.
*
* @param id id
*/
public void setId(String id) {
this.id = id;
}
/**
* Getter of documentId.
*
* @return the documentId
*/
public String getDocumentId() {
return documentId;
}
/**
* Setter of documentId.
*
* @param documentId documentId
*/
public void setDocumentId(String documentId) {
this.documentId = documentId;
}
/**
* Getter of createDate.
*
* @return the createDate
*/
public Date getCreateDate() {
return createDate;
}
/**
* Setter of createDate.
*
* @param createDate createDate
*/
public void setCreateDate(Date createDate) {
this.createDate = createDate;
}
/**
* Getter of deleteDate.
*
* @return the deleteDate
*/
@Override
public Date getDeleteDate() {
return deleteDate;
}
/**
* Setter of deleteDate.
*
* @param deleteDate deleteDate
*/
public void setDeleteDate(Date deleteDate) {
this.deleteDate = deleteDate;
}
/**
* Getter of content.
*
* @return the content
*/
public String getContent() {
return content;
}
/**
* Setter of content.
*
* @param content content
*/
public void setContent(String content) {
this.content = content;
}
/**
* Getter of userId.
*
* @return the userId
*/
public String getUserId() {
return userId;
}
/**
* Setter of userId.
*
* @param userId userId
*/
public void setUserId(String userId) {
this.userId = userId;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("id", id)
.add("documentId", documentId)
.add("userId", userId)
.toString();
}
@Override
public String toMessage() {
return documentId;
}
}

View File

@ -4,12 +4,10 @@ import java.util.Date;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.EntityListeners;
import javax.persistence.Id; import javax.persistence.Id;
import javax.persistence.Table; import javax.persistence.Table;
import com.google.common.base.MoreObjects; import com.google.common.base.MoreObjects;
import com.sismics.docs.core.util.AuditLogUtil;
/** /**
* Document entity. * Document entity.
@ -17,7 +15,6 @@ import com.sismics.docs.core.util.AuditLogUtil;
* @author bgamard * @author bgamard
*/ */
@Entity @Entity
@EntityListeners(AuditLogUtil.class)
@Table(name = "T_DOCUMENT") @Table(name = "T_DOCUMENT")
public class Document implements Loggable { public class Document implements Loggable {
/** /**

View File

@ -4,13 +4,11 @@ import java.util.Date;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.EntityListeners;
import javax.persistence.Id; import javax.persistence.Id;
import javax.persistence.Lob; import javax.persistence.Lob;
import javax.persistence.Table; import javax.persistence.Table;
import com.google.common.base.MoreObjects; import com.google.common.base.MoreObjects;
import com.sismics.docs.core.util.AuditLogUtil;
/** /**
* File entity. * File entity.
@ -18,7 +16,6 @@ import com.sismics.docs.core.util.AuditLogUtil;
* @author bgamard * @author bgamard
*/ */
@Entity @Entity
@EntityListeners(AuditLogUtil.class)
@Table(name = "T_FILE") @Table(name = "T_FILE")
public class File implements Loggable { public class File implements Loggable {
/** /**

View File

@ -4,12 +4,10 @@ import java.util.Date;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.EntityListeners;
import javax.persistence.Id; import javax.persistence.Id;
import javax.persistence.Table; import javax.persistence.Table;
import com.google.common.base.MoreObjects; import com.google.common.base.MoreObjects;
import com.sismics.docs.core.util.AuditLogUtil;
/** /**
* Tag. * Tag.
@ -17,7 +15,6 @@ import com.sismics.docs.core.util.AuditLogUtil;
* @author bgamard * @author bgamard
*/ */
@Entity @Entity
@EntityListeners(AuditLogUtil.class)
@Table(name = "T_TAG") @Table(name = "T_TAG")
public class Tag implements Loggable { public class Tag implements Loggable {
/** /**

View File

@ -4,12 +4,10 @@ import java.util.Date;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.EntityListeners;
import javax.persistence.Id; import javax.persistence.Id;
import javax.persistence.Table; import javax.persistence.Table;
import com.google.common.base.MoreObjects; import com.google.common.base.MoreObjects;
import com.sismics.docs.core.util.AuditLogUtil;
/** /**
* User entity. * User entity.
@ -17,7 +15,6 @@ import com.sismics.docs.core.util.AuditLogUtil;
* @author jtremeaux * @author jtremeaux
*/ */
@Entity @Entity
@EntityListeners(AuditLogUtil.class)
@Table(name = "T_USER") @Table(name = "T_USER")
public class User implements Loggable { public class User implements Loggable {
/** /**

View File

@ -5,17 +5,5 @@
version="2.0"> version="2.0">
<persistence-unit name="transactions-optional" transaction-type="RESOURCE_LOCAL"> <persistence-unit name="transactions-optional" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider> <provider>org.hibernate.ejb.HibernatePersistence</provider>
<class>com.sismics.docs.core.model.jpa.AuthenticationToken</class>
<class>com.sismics.docs.core.model.jpa.BaseFunction</class>
<class>com.sismics.docs.core.model.jpa.Config</class>
<class>com.sismics.docs.core.model.jpa.User</class>
<class>com.sismics.docs.core.model.jpa.RoleBaseFunction</class>
<class>com.sismics.docs.core.model.jpa.Document</class>
<class>com.sismics.docs.core.model.jpa.Tag</class>
<class>com.sismics.docs.core.model.jpa.DocumentTag</class>
<class>com.sismics.docs.core.model.jpa.Share</class>
<class>com.sismics.docs.core.model.jpa.Acl</class>
<class>com.sismics.docs.core.model.jpa.AuditLog</class>
</persistence-unit> </persistence-unit>
</persistence> </persistence>

View File

@ -1 +1 @@
db.version=2 db.version=3

View File

@ -0,0 +1,4 @@
create cached table T_COMMENT ( COM_ID_C varchar(36) not null, COM_IDDOC_C varchar(36) not null, COM_IDUSER_C varchar(36) not null, COM_CONTENT_C varchar(4000) not null, COM_CREATEDATE_D datetime, COM_DELETEDATE_D datetime, primary key (COM_ID_C) );
alter table T_COMMENT add constraint FK_COM_IDDOC_C foreign key (COM_IDDOC_C) references T_DOCUMENT (DOC_ID_C) on delete restrict on update restrict;
alter table T_COMMENT add constraint FK_COM_IDUSER_C foreign key (COM_IDUSER_C) references T_USER (USE_ID_C) on delete restrict on update restrict;
update T_CONFIG set CFG_VALUE_C = '3' where CFG_ID_C = 'DB_VERSION';

View File

@ -25,14 +25,14 @@
<org.slf4j.version>1.6.4</org.slf4j.version> <org.slf4j.version>1.6.4</org.slf4j.version>
<org.slf4j.jcl-over-slf4j.version>1.6.6</org.slf4j.jcl-over-slf4j.version> <org.slf4j.jcl-over-slf4j.version>1.6.6</org.slf4j.jcl-over-slf4j.version>
<junit.junit.version>4.7</junit.junit.version> <junit.junit.version>4.7</junit.junit.version>
<com.h2database.h2.version>1.4.188</com.h2database.h2.version> <com.h2database.h2.version>1.4.190</com.h2database.h2.version>
<org.glassfish.jersey.version>2.21</org.glassfish.jersey.version> <org.glassfish.jersey.version>2.22.1</org.glassfish.jersey.version>
<org.mindrot.jbcrypt>0.3m</org.mindrot.jbcrypt> <org.mindrot.jbcrypt>0.3m</org.mindrot.jbcrypt>
<org.apache.lucene.version>4.2.0</org.apache.lucene.version> <org.apache.lucene.version>4.2.0</org.apache.lucene.version>
<org.imgscalr.imgscalr-lib.version>4.2</org.imgscalr.imgscalr-lib.version> <org.imgscalr.imgscalr-lib.version>4.2</org.imgscalr.imgscalr-lib.version>
<org.apache.pdfbox.pdfbox.version>2.0.0-SNAPSHOT</org.apache.pdfbox.pdfbox.version> <org.apache.pdfbox.pdfbox.version>2.0.0-RC1</org.apache.pdfbox.pdfbox.version>
<org.bouncycastle.bcprov-jdk15on.version>1.49</org.bouncycastle.bcprov-jdk15on.version> <org.bouncycastle.bcprov-jdk15on.version>1.53</org.bouncycastle.bcprov-jdk15on.version>
<joda-time.joda-time.version>2.8.2</joda-time.joda-time.version> <joda-time.joda-time.version>2.9</joda-time.joda-time.version>
<org.hibernate.hibernate.version>4.1.0.Final</org.hibernate.hibernate.version> <org.hibernate.hibernate.version>4.1.0.Final</org.hibernate.hibernate.version>
<javax.servlet.javax.servlet-api.version>3.1.0</javax.servlet.javax.servlet-api.version> <javax.servlet.javax.servlet-api.version>3.1.0</javax.servlet.javax.servlet-api.version>
<com.levigo.jbig2.levigo-jbig2-imageio.version>1.6.3</com.levigo.jbig2.levigo-jbig2-imageio.version> <com.levigo.jbig2.levigo-jbig2-imageio.version>1.6.3</com.levigo.jbig2.levigo-jbig2-imageio.version>

View File

@ -1,3 +1,3 @@
api.current_version=${project.version} api.current_version=${project.version}
api.min_version=1.0 api.min_version=1.0
db.version=2 db.version=3

View File

@ -0,0 +1,150 @@
package com.sismics.docs.rest.resource;
import java.util.List;
import javax.json.Json;
import javax.json.JsonArrayBuilder;
import javax.json.JsonObjectBuilder;
import javax.ws.rs.DELETE;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import com.sismics.docs.core.constant.PermType;
import com.sismics.docs.core.dao.jpa.CommentDao;
import com.sismics.docs.core.dao.jpa.DocumentDao;
import com.sismics.docs.core.dao.jpa.dto.CommentDto;
import com.sismics.docs.core.model.jpa.Comment;
import com.sismics.rest.exception.ForbiddenClientException;
import com.sismics.rest.util.ValidationUtil;
/**
* Comment REST resource.
*
* @author bgamard
*/
@Path("/comment")
public class CommentResource extends BaseResource {
/**
* Add a comment.
*
* @param documentId Document ID
* @param content Comment content
* @return Response
*/
@PUT
public Response add(@FormParam("id") String documentId,
@FormParam("content") String content) {
if (!authenticate()) {
throw new ForbiddenClientException();
}
// Validate input data
ValidationUtil.validateRequired(documentId, "id");
content = ValidationUtil.validateLength(content, "content", 1, 4000, false);
// Read access on doc gives access to write comments
DocumentDao documentDao = new DocumentDao();
if (documentDao.getDocument(documentId, PermType.READ, principal.getId()) == null) {
return Response.status(Status.NOT_FOUND).build();
}
// Create the comment
Comment comment = new Comment();
comment.setDocumentId(documentId);
comment.setContent(content);
comment.setUserId(principal.getId());
CommentDao commentDao = new CommentDao();
commentDao.create(comment);
// Returns the comment
JsonObjectBuilder response = Json.createObjectBuilder()
.add("id", comment.getId())
.add("creator", principal.getName())
.add("content", comment.getContent())
.add("create_date", comment.getCreateDate().getTime());
return Response.ok().entity(response.build()).build();
}
/**
* Delete a comment.
*
* @param id Comment ID
* @return Response
*/
@DELETE
@Path("{id: [a-z0-9\\-]+}")
public Response delete(@PathParam("id") String id) {
if (!authenticate()) {
throw new ForbiddenClientException();
}
// Validate input data
ValidationUtil.validateRequired(id, "id");
// Get the comment
CommentDao commentDao = new CommentDao();
Comment comment = commentDao.getActiveById(id);
if (comment == null) {
return Response.status(Status.NOT_FOUND).build();
}
// If the current user owns the comment, skip ACL check
if (!comment.getUserId().equals(principal.getId())) {
// Get the associated document
DocumentDao documentDao = new DocumentDao();
if (documentDao.getDocument(comment.getDocumentId(), PermType.WRITE, principal.getId()) == null) {
return Response.status(Status.NOT_FOUND).build();
}
}
// Delete the comment
commentDao.delete(id);
// Always return OK
JsonObjectBuilder response = Json.createObjectBuilder()
.add("status", "ok");
return Response.ok().entity(response.build()).build();
}
/**
* Get all comments on a document.
*
* @param documentId DocumentID
* @return Response
*/
@GET
@Path("{documentId: [a-z0-9\\-]+}")
public Response get(@PathParam("documentId") String documentId) {
if (!authenticate()) {
throw new ForbiddenClientException();
}
// Read access on doc gives access to read comments
DocumentDao documentDao = new DocumentDao();
if (documentDao.getDocument(documentId, PermType.READ, principal.getId()) == null) {
return Response.status(Status.NOT_FOUND).build();
}
// Assemble results
CommentDao commentDao = new CommentDao();
List<CommentDto> commentDtoList = commentDao.getByDocumentId(documentId);
JsonArrayBuilder comments = Json.createArrayBuilder();
for (CommentDto commentDto : commentDtoList) {
comments.add(Json.createObjectBuilder()
.add("id", commentDto.getId())
.add("content", commentDto.getContent())
.add("creator", commentDto.getCreatorName())
.add("create_date", commentDto.getCreateTimestamp()));
}
// Always return OK
JsonObjectBuilder response = Json.createObjectBuilder()
.add("comments", comments);
return Response.ok().entity(response.build()).build();
}
}

View File

@ -11,7 +11,6 @@ import java.util.UUID;
import javax.json.Json; import javax.json.Json;
import javax.json.JsonArrayBuilder; import javax.json.JsonArrayBuilder;
import javax.json.JsonObjectBuilder; import javax.json.JsonObjectBuilder;
import javax.persistence.NoResultException;
import javax.ws.rs.DELETE; import javax.ws.rs.DELETE;
import javax.ws.rs.FormParam; import javax.ws.rs.FormParam;
import javax.ws.rs.GET; import javax.ws.rs.GET;
@ -82,17 +81,15 @@ public class DocumentResource extends BaseResource {
DocumentDao documentDao = new DocumentDao(); DocumentDao documentDao = new DocumentDao();
AclDao aclDao = new AclDao(); AclDao aclDao = new AclDao();
DocumentDto documentDto; DocumentDto documentDto = documentDao.getDocument(documentId);
try { if (documentDto == null) {
documentDto = documentDao.getDocument(documentId);
// Check document visibility
if (!aclDao.checkPermission(documentId, PermType.READ, shareId == null ? principal.getId() : shareId)) {
throw new ForbiddenClientException();
}
} catch (NoResultException e) {
return Response.status(Status.NOT_FOUND).build(); return Response.status(Status.NOT_FOUND).build();
} }
// Check document visibility
if (!aclDao.checkPermission(documentId, PermType.READ, shareId == null ? principal.getId() : shareId)) {
throw new ForbiddenClientException();
}
JsonObjectBuilder document = Json.createObjectBuilder() JsonObjectBuilder document = Json.createObjectBuilder()
.add("id", documentDto.getId()) .add("id", documentDto.getId())
@ -415,9 +412,8 @@ public class DocumentResource extends BaseResource {
// Get the document // Get the document
DocumentDao documentDao = new DocumentDao(); DocumentDao documentDao = new DocumentDao();
Document document; Document document;
try { document = documentDao.getDocument(id, PermType.WRITE, principal.getId());
document = documentDao.getDocument(id, PermType.WRITE, principal.getId()); if (document == null) {
} catch (NoResultException e) {
return Response.status(Status.NOT_FOUND).build(); return Response.status(Status.NOT_FOUND).build();
} }
@ -492,12 +488,9 @@ public class DocumentResource extends BaseResource {
// Get the document // Get the document
DocumentDao documentDao = new DocumentDao(); DocumentDao documentDao = new DocumentDao();
FileDao fileDao = new FileDao(); FileDao fileDao = new FileDao();
Document document; Document document = documentDao.getDocument(id, PermType.WRITE, principal.getId());
List<File> fileList; List<File> fileList = fileDao.getByDocumentId(principal.getId(), id);
try { if (document == null) {
document = documentDao.getDocument(id, PermType.WRITE, principal.getId());
fileList = fileDao.getByDocumentId(principal.getId(), id);
} catch (NoResultException e) {
return Response.status(Status.NOT_FOUND).build(); return Response.status(Status.NOT_FOUND).build();
} }

View File

@ -16,7 +16,6 @@ import java.util.zip.ZipOutputStream;
import javax.json.Json; import javax.json.Json;
import javax.json.JsonArrayBuilder; import javax.json.JsonArrayBuilder;
import javax.json.JsonObjectBuilder; import javax.json.JsonObjectBuilder;
import javax.persistence.NoResultException;
import javax.ws.rs.Consumes; import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE; import javax.ws.rs.DELETE;
import javax.ws.rs.FormParam; import javax.ws.rs.FormParam;
@ -98,9 +97,8 @@ public class FileResource extends BaseResource {
documentId = null; documentId = null;
} else { } else {
DocumentDao documentDao = new DocumentDao(); DocumentDao documentDao = new DocumentDao();
try { document = documentDao.getDocument(documentId, PermType.WRITE, principal.getId());
document = documentDao.getDocument(documentId, PermType.WRITE, principal.getId()); if (document == null) {
} catch (NoResultException e) {
return Response.status(Status.NOT_FOUND).build(); return Response.status(Status.NOT_FOUND).build();
} }
} }
@ -190,12 +188,9 @@ public class FileResource extends BaseResource {
// Get the document and the file // Get the document and the file
DocumentDao documentDao = new DocumentDao(); DocumentDao documentDao = new DocumentDao();
FileDao fileDao = new FileDao(); FileDao fileDao = new FileDao();
Document document; File file = fileDao.getFile(id, principal.getId());
File file; Document document = documentDao.getDocument(documentId, PermType.WRITE, principal.getId());
try { if (file == null || document == null) {
file = fileDao.getFile(id, principal.getId());
document = documentDao.getDocument(documentId, PermType.WRITE, principal.getId());
} catch (NoResultException e) {
return Response.status(Status.NOT_FOUND).build(); return Response.status(Status.NOT_FOUND).build();
} }
@ -251,9 +246,7 @@ public class FileResource extends BaseResource {
// Get the document // Get the document
DocumentDao documentDao = new DocumentDao(); DocumentDao documentDao = new DocumentDao();
try { if (documentDao.getDocument(documentId, PermType.WRITE, principal.getId()) == null) {
documentDao.getDocument(documentId, PermType.WRITE, principal.getId());
} catch (NoResultException e) {
return Response.status(Status.NOT_FOUND).build(); return Response.status(Status.NOT_FOUND).build();
} }
@ -330,19 +323,18 @@ public class FileResource extends BaseResource {
// Get the file // Get the file
FileDao fileDao = new FileDao(); FileDao fileDao = new FileDao();
DocumentDao documentDao = new DocumentDao(); DocumentDao documentDao = new DocumentDao();
File file; File file = fileDao.getFile(id);
try { if (file == null) {
file = fileDao.getFile(id); return Response.status(Status.NOT_FOUND).build();
if (file.getDocumentId() == null) { }
// It's an orphan file
if (!file.getUserId().equals(principal.getId())) { if (file.getDocumentId() == null) {
// But not ours // It's an orphan file
throw new ForbiddenClientException(); if (!file.getUserId().equals(principal.getId())) {
} // But not ours
} else { throw new ForbiddenClientException();
documentDao.getDocument(file.getDocumentId(), PermType.WRITE, principal.getId());
} }
} catch (NoResultException e) { } else if (documentDao.getDocument(file.getDocumentId(), PermType.WRITE, principal.getId()) == null) {
return Response.status(Status.NOT_FOUND).build(); return Response.status(Status.NOT_FOUND).build();
} }
@ -383,26 +375,24 @@ public class FileResource extends BaseResource {
// Get the file // Get the file
FileDao fileDao = new FileDao(); FileDao fileDao = new FileDao();
UserDao userDao = new UserDao(); UserDao userDao = new UserDao();
File file; File file = fileDao.getFile(fileId);
try { if (file == null) {
file = fileDao.getFile(fileId);
if (file.getDocumentId() == null) {
// It's an orphan file
if (!file.getUserId().equals(principal.getId())) {
// But not ours
throw new ForbiddenClientException();
}
} else {
// Check document accessibility
AclDao aclDao = new AclDao();
if (!aclDao.checkPermission(file.getDocumentId(), PermType.READ, shareId == null ? principal.getId() : shareId)) {
throw new ForbiddenClientException();
}
}
} catch (NoResultException e) {
return Response.status(Status.NOT_FOUND).build(); return Response.status(Status.NOT_FOUND).build();
} }
if (file.getDocumentId() == null) {
// It's an orphan file
if (!file.getUserId().equals(principal.getId())) {
// But not ours
throw new ForbiddenClientException();
}
} else {
// Check document accessibility
AclDao aclDao = new AclDao();
if (!aclDao.checkPermission(file.getDocumentId(), PermType.READ, shareId == null ? principal.getId() : shareId)) {
throw new ForbiddenClientException();
}
}
// Get the stored file // Get the stored file
@ -470,19 +460,17 @@ public class FileResource extends BaseResource {
// Get the document // Get the document
DocumentDao documentDao = new DocumentDao(); DocumentDao documentDao = new DocumentDao();
DocumentDto documentDto; DocumentDto documentDto = documentDao.getDocument(documentId);
try { if (documentDto == null) {
documentDto = documentDao.getDocument(documentId);
// Check document visibility
AclDao aclDao = new AclDao();
if (!aclDao.checkPermission(documentId, PermType.READ, shareId == null ? principal.getId() : shareId)) {
throw new ForbiddenClientException();
}
} catch (NoResultException e) {
return Response.status(Status.NOT_FOUND).build(); return Response.status(Status.NOT_FOUND).build();
} }
// Check document visibility
AclDao aclDao = new AclDao();
if (!aclDao.checkPermission(documentId, PermType.READ, shareId == null ? principal.getId() : shareId)) {
throw new ForbiddenClientException();
}
// Get files and user associated with this document // Get files and user associated with this document
FileDao fileDao = new FileDao(); FileDao fileDao = new FileDao();
final UserDao userDao = new UserDao(); final UserDao userDao = new UserDao();

View File

@ -6,13 +6,13 @@ import java.util.List;
import javax.json.Json; import javax.json.Json;
import javax.json.JsonObjectBuilder; import javax.json.JsonObjectBuilder;
import javax.persistence.NoResultException;
import javax.ws.rs.DELETE; import javax.ws.rs.DELETE;
import javax.ws.rs.FormParam; import javax.ws.rs.FormParam;
import javax.ws.rs.PUT; import javax.ws.rs.PUT;
import javax.ws.rs.Path; import javax.ws.rs.Path;
import javax.ws.rs.PathParam; import javax.ws.rs.PathParam;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import com.sismics.docs.core.constant.AclTargetType; import com.sismics.docs.core.constant.AclTargetType;
import com.sismics.docs.core.constant.PermType; import com.sismics.docs.core.constant.PermType;
@ -53,10 +53,8 @@ public class ShareResource extends BaseResource {
// Get the document // Get the document
DocumentDao documentDao = new DocumentDao(); DocumentDao documentDao = new DocumentDao();
try { if (documentDao.getDocument(documentId, PermType.WRITE, principal.getId()) == null) {
documentDao.getDocument(documentId, PermType.WRITE, principal.getId()); return Response.status(Status.NOT_FOUND).build();
} catch (NoResultException e) {
throw new ClientException("DocumentNotFound", MessageFormat.format("Document not found: {0}", documentId));
} }
// Create the share // Create the share

View File

@ -17,6 +17,9 @@
<span ng-switch-when="File"> <span ng-switch-when="File">
<a ng-href="#/document/view/{{ log.message }}/file/{{ log.target }}">Open</a> <a ng-href="#/document/view/{{ log.message }}/file/{{ log.target }}">Open</a>
</span> </span>
<span ng-switch-when="Comment">
<a ng-href="#/document/view/{{ log.message }}/comments">See</a>
</span>
<span ng-switch-when="Acl"> <span ng-switch-when="Acl">
{{ log.message }} {{ log.message }}
</span> </span>

View File

@ -1,3 +1,3 @@
api.current_version=${project.version} api.current_version=${project.version}
api.min_version=1.0 api.min_version=1.0
db.version=2 db.version=3

View File

@ -1,3 +1,3 @@
api.current_version=${project.version} api.current_version=${project.version}
api.min_version=1.0 api.min_version=1.0
db.version=2 db.version=3

View File

@ -12,7 +12,6 @@ import org.junit.Test;
import com.sismics.util.filter.TokenBasedSecurityFilter; import com.sismics.util.filter.TokenBasedSecurityFilter;
/** /**
* Test the audit log resource. * Test the audit log resource.
* *

View File

@ -0,0 +1,143 @@
package com.sismics.docs.rest;
import java.util.Date;
import javax.json.JsonObject;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Form;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import org.junit.Assert;
import org.junit.Test;
import com.sismics.util.filter.TokenBasedSecurityFilter;
/**
* Exhaustive test of the comment resource.
*
* @author bgamard
*/
public class TestCommentResource extends BaseJerseyTest {
/**
* Test the comment resource.
*
* @throws Exception
*/
@Test
public void testCommentResource() throws Exception {
// Login comment1
clientUtil.createUser("comment1");
String comment1Token = clientUtil.login("comment1");
// Login comment2
clientUtil.createUser("comment2");
String comment2Token = clientUtil.login("comment2");
// Create a document with comment1
long create1Date = new Date().getTime();
JsonObject json = target().path("/document").request()
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, comment1Token)
.put(Entity.form(new Form()
.param("title", "My super title document 1")
.param("description", "My super description for document 1")
.param("language", "eng")
.param("create_date", Long.toString(create1Date))), JsonObject.class);
String document1Id = json.getString("id");
Assert.assertNotNull(document1Id);
// Create a comment with comment2 (fail, no read access)
Response response = target().path("/comment").request()
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, comment2Token)
.put(Entity.form(new Form()
.param("id", document1Id)
.param("content", "Comment by comment2")));
Assert.assertEquals(Status.NOT_FOUND, Status.fromStatusCode(response.getStatus()));
// Read comments with comment2 (fail, no read access)
response = target().path("/comment/" + document1Id).request()
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, comment2Token)
.get();
Assert.assertEquals(Status.NOT_FOUND, Status.fromStatusCode(response.getStatus()));
// Read comments with comment 1
json = target().path("/comment/" + document1Id).request()
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, comment1Token)
.get(JsonObject.class);
Assert.assertEquals(0, json.getJsonArray("comments").size());
// Create a comment with comment1
json = target().path("/comment").request()
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, comment1Token)
.put(Entity.form(new Form()
.param("id", document1Id)
.param("content", "Comment by comment1")), JsonObject.class);
String comment1Id = json.getString("id");
Assert.assertNotNull(comment1Id);
Assert.assertEquals("Comment by comment1", json.getString("content"));
Assert.assertEquals("comment1", json.getString("creator"));
Assert.assertNotNull(json.getJsonNumber("create_date"));
// Read comments with comment1
json = target().path("/comment/" + document1Id).request()
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, comment1Token)
.get(JsonObject.class);
Assert.assertEquals(1, json.getJsonArray("comments").size());
Assert.assertEquals(comment1Id, json.getJsonArray("comments").getJsonObject(0).getString("id"));
// Delete a comment with comment2 (fail, no write access)
response = target().path("/comment/" + comment1Id).request()
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, comment2Token)
.delete();
Assert.assertEquals(Status.NOT_FOUND, Status.fromStatusCode(response.getStatus()));
// Delete a comment with comment1
json = target().path("/comment/" + comment1Id).request()
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, comment1Token)
.delete(JsonObject.class);
// Read comments with comment1
json = target().path("/comment/" + document1Id).request()
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, comment1Token)
.get(JsonObject.class);
Assert.assertEquals(0, json.getJsonArray("comments").size());
// Add an ACL READ for comment2 with comment1
json = target().path("/acl").request()
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, comment1Token)
.put(Entity.form(new Form()
.param("source", document1Id)
.param("perm", "READ")
.param("username", "comment2")), JsonObject.class);
// Create a comment with comment2
json = target().path("/comment").request()
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, comment2Token)
.put(Entity.form(new Form()
.param("id", document1Id)
.param("content", "Comment by comment2")), JsonObject.class);
String comment2Id = json.getString("id");
// Read comments with comment2
json = target().path("/comment/" + document1Id).request()
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, comment2Token)
.get(JsonObject.class);
Assert.assertEquals(1, json.getJsonArray("comments").size());
JsonObject comment = json.getJsonArray("comments").getJsonObject(0);
Assert.assertEquals(comment2Id, comment.getString("id"));
Assert.assertEquals("Comment by comment2", comment.getString("content"));
Assert.assertEquals("comment2", comment.getString("creator"));
Assert.assertNotNull(comment.getJsonNumber("create_date"));
// Delete a comment with comment2
json = target().path("/comment/" + comment2Id).request()
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, comment2Token)
.delete(JsonObject.class);
// Read comments with comment2
json = target().path("/comment/" + document1Id).request()
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, comment2Token)
.get(JsonObject.class);
Assert.assertEquals(0, json.getJsonArray("comments").size());
}
}

View File

@ -26,8 +26,6 @@ import com.sismics.util.filter.TokenBasedSecurityFilter;
import com.sismics.util.mime.MimeType; import com.sismics.util.mime.MimeType;
import com.sismics.util.mime.MimeTypeUtil; import com.sismics.util.mime.MimeTypeUtil;
/** /**
* Exhaustive test of the document resource. * Exhaustive test of the document resource.
* *

View File

@ -27,7 +27,6 @@ import com.sismics.util.filter.TokenBasedSecurityFilter;
import com.sismics.util.mime.MimeType; import com.sismics.util.mime.MimeType;
import com.sismics.util.mime.MimeTypeUtil; import com.sismics.util.mime.MimeTypeUtil;
/** /**
* Exhaustive test of the file resource. * Exhaustive test of the file resource.
* *

View File

@ -20,7 +20,6 @@ import com.google.common.io.ByteStreams;
import com.google.common.io.Resources; import com.google.common.io.Resources;
import com.sismics.util.filter.TokenBasedSecurityFilter; import com.sismics.util.filter.TokenBasedSecurityFilter;
/** /**
* Exhaustive test of the share resource. * Exhaustive test of the share resource.
* *

View File

@ -13,7 +13,6 @@ import org.junit.Test;
import com.sismics.util.filter.TokenBasedSecurityFilter; import com.sismics.util.filter.TokenBasedSecurityFilter;
/** /**
* Test the tag resource. * Test the tag resource.
* *

View File

@ -14,7 +14,6 @@ import org.junit.Test;
import com.sismics.util.filter.TokenBasedSecurityFilter; import com.sismics.util.filter.TokenBasedSecurityFilter;
/** /**
* Exhaustive test of the user resource. * Exhaustive test of the user resource.
* *