This is an automated email from the git hooks/post-receive script. New commit to branch develop in repository coselmar. See http://git.codelutin.com/coselmar.git commit 81fb833326127ce16207d00d786e7f29e63a2934 Author: Yannick Martel <martel@©odelutin.com> Date: Wed Nov 26 18:18:51 2014 +0100 #6142 #6143 only Admin, supervisor, expert (public) or admin, supervisor, owner can view and delete document --- .../services/CoselmarWebServiceSupport.java | 13 +++ .../coselmar/services/v1/DocumentsWebService.java | 128 ++++++++++++++++++--- coselmar-ui/src/main/webapp/index.html | 1 + .../src/main/webapp/js/coselmar-controllers.js | 2 +- .../main/webapp/views/documents/newdocument.html | 17 +-- 5 files changed, 133 insertions(+), 28 deletions(-) diff --git a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/CoselmarWebServiceSupport.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/CoselmarWebServiceSupport.java index 5213d75..6fc9115 100644 --- a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/CoselmarWebServiceSupport.java +++ b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/CoselmarWebServiceSupport.java @@ -35,6 +35,7 @@ import java.util.Map; import com.auth0.jwt.JWTVerifier; import fr.ifremer.coselmar.beans.UserWebToken; import fr.ifremer.coselmar.persistence.CoselmarPersistenceContext; +import fr.ifremer.coselmar.persistence.entity.CoselmarUser; import fr.ifremer.coselmar.persistence.entity.CoselmarUserTopiaDao; import fr.ifremer.coselmar.persistence.entity.DocumentTopiaDao; import fr.ifremer.coselmar.services.config.CoselmarServicesConfig; @@ -147,4 +148,16 @@ public abstract class CoselmarWebServiceSupport extends WebMotionController impl } } + + /** + * Construct the full id of an user from the random part contained in + * {@link fr.ifremer.coselmar.beans.UserWebToken}. + * + * @param shortUserId : the short id, corresponding to the random part of the id + * + * @return the complete id, with class FQN. + */ + protected String getFullUserIdFromShort(String shortUserId) { + return CoselmarUser.class.getCanonicalName() + getPersistenceContext().getTopiaIdFactory().getSeparator() + shortUserId; + } } diff --git a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/DocumentsWebService.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/DocumentsWebService.java index e3380b8..bde35f3 100644 --- a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/DocumentsWebService.java +++ b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/DocumentsWebService.java @@ -63,15 +63,40 @@ public class DocumentsWebService extends CoselmarWebServiceSupport { private static final Log log = getLog(DocumentsWebService.class); - public static final List<String> DOCUMENT_ALLOWED_USER_ROLES = + public static final List<String> DOCUMENT_EDIT_ALLOWED_USER_ROLES = Lists.newArrayList(CoselmarUserRole.EXPERT.name()); - public DocumentBean getDocument(String documentId) { + public static final List<String> DOCUMENT_VIEW_ALLOWED_USER_ROLES = + Lists.newArrayList( + CoselmarUserRole.ADMIN.name(), + CoselmarUserRole.SUPERVISOR.name(), + CoselmarUserRole.EXPERT.name() + ); + + public DocumentBean getDocument(String documentId) throws InvalidCredentialException, UnauthorizedException { + + // Check authentication + String authorization = getContext().getHeader("Authorization"); + UserWebToken userWebToken = checkAuthentication(authorization); // reconstitute full id - String fullId = Document.class.getCanonicalName() + getPersistenceContext().getTopiaIdFactory().getSeparator() + documentId; + String fullId = getDocumentFullId(documentId); Document document = getDocumentDao().forTopiaIdEquals(fullId).findUnique(); + + // If document if public, only admin, supervisor and expert could view it + // If document if private, only admin, supervisor and owner could view it + if (!isAllowedToAccessDocument(userWebToken, document)) { + + String message = String.format("User %s %s ('%s') try to access to document '%s'", + userWebToken.getFirstName(), userWebToken.getLastName(), userWebToken.getUserId(), documentId); + if (log.isWarnEnabled()) { + log.warn(message); + } + throw new UnauthorizedException(message); + + } + DocumentBean documentBean = BeanEntityConverter.toBean(documentId, document); return documentBean; } @@ -104,7 +129,7 @@ public class DocumentsWebService extends CoselmarWebServiceSupport { // Only Expert or Supervisor can add document String userRole = userWebToken.getRole(); - if (!DOCUMENT_ALLOWED_USER_ROLES.contains(userRole.toUpperCase())) { + if (!DOCUMENT_EDIT_ALLOWED_USER_ROLES.contains(userRole.toUpperCase())) { String message = String.format("User %s %s ('%s') is not allowed to add document", userWebToken.getFirstName(), userWebToken.getLastName(), userWebToken.getUserId()); if (log.isWarnEnabled()) { @@ -116,8 +141,7 @@ public class DocumentsWebService extends CoselmarWebServiceSupport { Preconditions.checkNotNull(document); // retrieve user who will be assigned as document owner - String fullId = CoselmarUser.class.getCanonicalName() + - getPersistenceContext().getTopiaIdFactory().getSeparator() + userWebToken.getUserId(); + String fullId = getFullUserIdFromShort(userWebToken.getUserId()); CoselmarUser owner; try { @@ -183,21 +207,15 @@ public class DocumentsWebService extends CoselmarWebServiceSupport { public Render getDocumentFile(String documentId) { // reconstitute full id - String fullId = Document.class.getCanonicalName() + "_" + documentId; + String fullId =getDocumentFullId(documentId); Document document = getDocumentDao().forTopiaIdEquals(fullId).findUnique(); //TODO ymartel 20141103 : manage file owner ? // Get file attached to document - String fileName = document.getName(); - Date depositeDate = document.getDepositDate(); - - String userDocumentPath = getUserDocumentPath(document.getOwner()); - - String formatedDay = DateUtil.formatDate(depositeDate, "yyyymmdd"); - String prefix = formatedDay + "-"; - File documentFile = new File(userDocumentPath + "/" + prefix + fileName); + File documentFile = getDocumentFile(document); + String fileName = document.getName(); String fileMimeType = document.getMimeType(); try { InputStream fileStream = new FileInputStream(documentFile); @@ -218,16 +236,35 @@ public class DocumentsWebService extends CoselmarWebServiceSupport { throw new CoselmarTechnicalException("not yet implemented"); } - public void deleteDocument(String documentId) { + public void deleteDocument(String documentId) throws InvalidCredentialException, UnauthorizedException { + + // Check authentication + String authorization = getContext().getHeader("Authorization"); + UserWebToken userWebToken = checkAuthentication(authorization); // reconstitute full id - String fullId = Document.class.getCanonicalName() + "_" + documentId; + String fullId = getDocumentFullId(documentId); Document document = getDocumentDao().forTopiaIdEquals(fullId).findUnique(); - getDocumentDao().delete(document); - //TODO ymartel 20141126 : delete file + if (!isAllowedToAccessDocument(userWebToken, document)) { + + String message = String.format("User %s %s ('%s') try to delete document '%s'", + userWebToken.getFirstName(), userWebToken.getLastName(), userWebToken.getUserId(), documentId); + if (log.isWarnEnabled()) { + log.warn(message); + } + throw new UnauthorizedException(message); + + } + + // Delete physical file + if (document.isWithFile()) { + File documentFile = getDocumentFile(document); + FileUtils.deleteQuietly(documentFile); + } + getDocumentDao().delete(document); commit(); } @@ -284,4 +321,57 @@ public class DocumentsWebService extends CoselmarWebServiceSupport { return userPath; } + /** + * Return the document File from its Metadata. + * + * @param document : document we want the {@link java.io.File} corresponding + * to the physical document. + * @return a File pointing out the physical file. + */ + protected File getDocumentFile(Document document) { + String fileName = document.getName(); + Date depositeDate = document.getDepositDate(); + + String userDocumentPath = getUserDocumentPath(document.getOwner()); + + String formatedDay = DateUtil.formatDate(depositeDate, "yyyymmdd"); + String prefix = formatedDay + "-"; + return new File(userDocumentPath + "/" + prefix + fileName); + } + + /** + * Check if an user can access to a document metadata, according to its + * privacy settings and the user grant. + * + * @param userWebToken : Session data for current user, + * containing {@link fr.ifremer.coselmar.persistence.entity.CoselmarUserRole} as String + * @param document : the document user trying to access. + * + * @return true is user can access, false else. + */ + protected boolean isAllowedToAccessDocument(UserWebToken userWebToken, Document document) { + boolean isAuthorized = false; + + String viewerRole = userWebToken.getRole().toUpperCase(); + + // For public : only admin/supervisor/expert can access + if (document.getPrivacy() == DocumentPrivacy.PUBLIC) { + isAuthorized = DOCUMENT_VIEW_ALLOWED_USER_ROLES.contains(viewerRole); + + // For Private : only admin/supervisor/owner can access + } else if (document.getPrivacy() == DocumentPrivacy.PRIVATE) { + CoselmarUser documentOwner = document.getOwner(); + boolean isOwner = StringUtils.equals(documentOwner.getTopiaId(), getFullUserIdFromShort(userWebToken.getUserId())); + isAuthorized = isOwner || Lists.newArrayList(CoselmarUserRole.ADMIN, CoselmarUserRole.SUPERVISOR).contains(viewerRole); + + } else { + // Restricted : Not yet implemented + } + + return isAuthorized; + } + + protected String getDocumentFullId(String documentId) { + return Document.class.getCanonicalName() + getPersistenceContext().getTopiaIdFactory().getSeparator() + documentId; + } } diff --git a/coselmar-ui/src/main/webapp/index.html b/coselmar-ui/src/main/webapp/index.html index 968466f..f8ac539 100644 --- a/coselmar-ui/src/main/webapp/index.html +++ b/coselmar-ui/src/main/webapp/index.html @@ -34,6 +34,7 @@ <script src="nuiton-js/angular-messages.js"></script> <script src="nuiton-js/angular-ui-bootstrap.js"></script> <script src="nuiton-js/bootstrap.js"></script> + <script src="nuiton-js/bootstrap-tpls.js"></script> <script src="js/angular-jwt.js"></script> <script src="js/coselmar.js"></script> <script src="js/coselmar-constants.js"></script> diff --git a/coselmar-ui/src/main/webapp/js/coselmar-controllers.js b/coselmar-ui/src/main/webapp/js/coselmar-controllers.js index 2be62f7..0a0a8cf 100644 --- a/coselmar-ui/src/main/webapp/js/coselmar-controllers.js +++ b/coselmar-ui/src/main/webapp/js/coselmar-controllers.js @@ -21,7 +21,7 @@ * <http://www.gnu.org/licenses/gpl-3.0.html>. * #L% */ -var coselmarControllers = angular.module('coselmarControllers', []); +var coselmarControllers = angular.module('coselmarControllers', ['ui.bootstrap']); // Controller when the main page/view loads coselmarControllers.controller("HomeCtrl", ['$scope', '$http', '$location', 'userService', 'jwtHelper', diff --git a/coselmar-ui/src/main/webapp/views/documents/newdocument.html b/coselmar-ui/src/main/webapp/views/documents/newdocument.html index d619818..f98ad7e 100644 --- a/coselmar-ui/src/main/webapp/views/documents/newdocument.html +++ b/coselmar-ui/src/main/webapp/views/documents/newdocument.html @@ -110,6 +110,15 @@ </div> + <div class="form-group"> + <label class="col-md-4 control-label">Publication date</label> + + <div class="col-md-5"> + <input type="text" class="form-control" name="publicationDate" ng-model="document.publicationDate" + datepicker-popup="dd/MM/yyyy" is-open="publicationDateOpened" ng-click="publicationDateOpened = true"/> + </div> + </div> + <div class="form-group"> <label class="col-md-4 control-label">Authors</label> @@ -143,14 +152,6 @@ </div> <div class="form-group"> - <label class="col-md-4 control-label">Publication date</label> - - <div class="col-md-5"> - <input type="date" class="form-control" name="publicationDate" ng-model="document.publicationDate" /> - </div> - </div> - - <div class="form-group"> <label class="col-md-4 control-label">Summary</label> <div class="col-md-5"> -- To stop receiving notification emails like this one, please contact codelutin.com SCM administrator <admin+scm@codelutin.com>.