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 ed571a6f9dbc7e14d48722362706cd25209ac28c Author: Yannick Martel <martel@©odelutin.com> Date: Tue Feb 3 18:23:47 2015 +0100 prepare service : user is authorized to see a restricted document he is in list --- .../services/CoselmarWebServiceSupport.java | 77 +++++++++++++++++++- .../coselmar/services/v1/DocumentsWebService.java | 85 +++++++++++----------- 2 files changed, 118 insertions(+), 44 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 373853b..e634047 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 @@ -34,6 +34,7 @@ import java.util.Map; import com.auth0.jwt.JWTVerifier; import fr.ifremer.coselmar.beans.UserWebToken; +import fr.ifremer.coselmar.config.CoselmarServicesConfig; import fr.ifremer.coselmar.exceptions.CoselmarTechnicalException; import fr.ifremer.coselmar.persistence.CoselmarPersistenceContext; import fr.ifremer.coselmar.persistence.entity.CoselmarUser; @@ -41,7 +42,6 @@ import fr.ifremer.coselmar.persistence.entity.CoselmarUserGroupTopiaDao; import fr.ifremer.coselmar.persistence.entity.CoselmarUserTopiaDao; import fr.ifremer.coselmar.persistence.entity.DocumentTopiaDao; import fr.ifremer.coselmar.persistence.entity.QuestionTopiaDao; -import fr.ifremer.coselmar.config.CoselmarServicesConfig; import fr.ifremer.coselmar.services.errors.InvalidCredentialException; import fr.ifremer.coselmar.services.v1.DocumentsWebService; import org.apache.commons.codec.binary.Base64; @@ -50,6 +50,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.debux.webmotion.server.WebMotionController; import org.debux.webmotion.server.call.HttpContext; +import org.nuiton.topia.persistence.TopiaNoResultException; /** * @author ymartel <martel@codelutin.com> @@ -132,6 +133,18 @@ public abstract class CoselmarWebServiceSupport extends WebMotionController impl getPersistenceContext().rollback(); } + /** + * Check the authorization code. + * Get the token from the authorization and reconstitute JWT user token. + * + * @param authorization : authorization containing the encoding token + * @return corresponding {@link fr.ifremer.coselmar.beans.UserWebToken} + * + * @throws InvalidCredentialException if token is not valid. + * + * @Deprecated since 0.8 : prefer use {@link #checkUserAuthentication(String)} that also check user validity. + */ + @Deprecated protected UserWebToken checkAuthentication(String authorization) throws InvalidCredentialException { try { @@ -161,6 +174,57 @@ public abstract class CoselmarWebServiceSupport extends WebMotionController impl } /** + * Check the authorization code. + * Get the token from the authorization and reconstitute JWT user token. + * From this token, retrieve the user from database. + * + * @param authorization : authorization containing the encoding token + * + * @return corresponding {@link fr.ifremer.coselmar.persistence.entity.CoselmarUser} + * + * @throws InvalidCredentialException if token is not valid or if user does not exist. + */ + protected CoselmarUser checkUserAuthentication(String authorization) throws InvalidCredentialException { + try { + + String webSecurityKey = getServicesContext().getCoselmarServicesConfig().getWebSecurityKey(); + JWTVerifier jwtVerifier = new JWTVerifier(Base64.encodeBase64String(webSecurityKey.getBytes("utf8")), "audience"); + + String token = StringUtils.replace(authorization, "Bearer ", ""); + Map<String, Object> claims = jwtVerifier.verify(token); + UserWebToken userWebToken = new UserWebToken(claims); + + // check user still exist + String userId = getFullUserIdFromShort(userWebToken.getUserId()); + CoselmarUser coselmarUser = getCoselmarUserDao().forTopiaIdEquals(userId).findAny(); + + return coselmarUser; + + } catch (NoSuchAlgorithmException|InvalidKeyException|IOException e) { + // This should not happened or this is really exceptional ! + if (log.isErrorEnabled()) { + log.error("Error during JWT verification : wrong Algorithm !", e); + } + throw new CoselmarTechnicalException(e); + + } catch (SignatureException e) { + // Invalid Signature ! It's a Fake ! + if (log.isErrorEnabled()) { + log.error("Error during JWT verification : bad signature!", e); + } + throw new InvalidCredentialException("Error with signature"); + + } catch (TopiaNoResultException e) { + // User not found, maybe old token ? Or well faked ! + if (log.isErrorEnabled()) { + log.error("Error during authorization check : user does not exist !", e); + } + throw new InvalidCredentialException("User not known"); + } + + } + + /** * Construct the full id of an user from the random part contained in * {@link fr.ifremer.coselmar.beans.UserWebToken}. * @@ -183,4 +247,15 @@ public abstract class CoselmarWebServiceSupport extends WebMotionController impl protected String getFullIdFromShort(Class clazz, String shortId) { return clazz.getCanonicalName() + getPersistenceContext().getTopiaIdFactory().getSeparator() + shortId; } + + /** + * Construct a light id with only the random part. + * + * @param fullId : the full id from which extract the random part + * + * @return a light id. + */ + protected String getShortIdFromFull(String fullId) { + return getPersistenceContext().getTopiaIdFactory().getRandomPart(fullId); + } } 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 faf3a41..543a629 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 @@ -32,6 +32,7 @@ import java.io.InputStream; import java.util.ArrayList; import java.util.Date; import java.util.List; +import java.util.Set; import com.google.common.base.Function; import com.google.common.base.Preconditions; @@ -41,6 +42,7 @@ import fr.ifremer.coselmar.beans.QuestionBean; import fr.ifremer.coselmar.beans.UserWebToken; import fr.ifremer.coselmar.converter.BeanEntityConverter; import fr.ifremer.coselmar.persistence.entity.CoselmarUser; +import fr.ifremer.coselmar.persistence.entity.CoselmarUserGroup; import fr.ifremer.coselmar.persistence.entity.CoselmarUserRole; import fr.ifremer.coselmar.persistence.entity.Document; import fr.ifremer.coselmar.persistence.entity.Privacy; @@ -70,8 +72,11 @@ public class DocumentsWebService extends CoselmarWebServiceSupport { private static final Log log = getLog(DocumentsWebService.class); + public static final List<String> DOCUMENT_CREATE_ALLOWED_USER_ROLES = + Lists.newArrayList(CoselmarUserRole.ADMIN.name(), CoselmarUserRole.SUPERVISOR.name(), CoselmarUserRole.EXPERT.name()); + public static final List<String> DOCUMENT_EDIT_ALLOWED_USER_ROLES = - Lists.newArrayList(CoselmarUserRole.ADMIN.name(), CoselmarUserRole.EXPERT.name(), CoselmarUserRole.SUPERVISOR.name()); + Lists.newArrayList(CoselmarUserRole.ADMIN.name(), CoselmarUserRole.SUPERVISOR.name()); public static final List<String> DOCUMENT_VIEW_ALLOWED_USER_ROLES = Lists.newArrayList( @@ -84,7 +89,7 @@ public class DocumentsWebService extends CoselmarWebServiceSupport { // Check authentication String authorization = getContext().getHeader("Authorization"); - UserWebToken userWebToken = checkAuthentication(authorization); + CoselmarUser currentUser = checkUserAuthentication(authorization); // reconstitute full id String fullId = getDocumentFullId(documentId); @@ -93,10 +98,10 @@ public class DocumentsWebService extends CoselmarWebServiceSupport { // 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)) { + if (!isAllowedToAccessDocument(currentUser, document)) { String message = String.format("User %s %s ('%s') try to access to document '%s'", - userWebToken.getFirstName(), userWebToken.getLastName(), userWebToken.getUserId(), documentId); + currentUser.getFirstname(), currentUser.getName(), getShortIdFromFull(currentUser.getTopiaId()), documentId); if (log.isWarnEnabled()) { log.warn(message); } @@ -180,7 +185,7 @@ public class DocumentsWebService extends CoselmarWebServiceSupport { // Only Expert or Supervisor can add document String userRole = userWebToken.getRole(); - if (!DOCUMENT_EDIT_ALLOWED_USER_ROLES.contains(userRole.toUpperCase())) { + if (!DOCUMENT_CREATE_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()) { @@ -278,41 +283,28 @@ public class DocumentsWebService extends CoselmarWebServiceSupport { public void addDocumentFile(String documentId, UploadFile uploadFile) throws InvalidCredentialException, UnauthorizedException { + + Preconditions.checkNotNull(documentId); + Preconditions.checkNotNull(uploadFile); + // Check authentication String authorization = getContext().getHeader("Authorization"); - UserWebToken userWebToken = checkAuthentication(authorization); + CoselmarUser currentUser = checkUserAuthentication(authorization); - // Only Expert or Supervisor can add document - String userRole = userWebToken.getRole(); - 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()); + String documentFullId = getFullIdFromShort(Document.class, documentId); + Document document = getDocumentDao().forTopiaIdEquals(documentFullId).findAny(); + + // Only Owner Expert or Supervisor/Admin can add document file + if (!DOCUMENT_EDIT_ALLOWED_USER_ROLES.contains(currentUser.getRole().name()) + && document.getOwner() != currentUser) { + String message = String.format("User %s %s ('%s') is not allowed to add document file", + currentUser.getFirstname(), currentUser.getName(), getShortIdFromFull(currentUser.getTopiaId())); if (log.isWarnEnabled()) { log.warn(message); } throw new UnauthorizedException(message); } - Preconditions.checkNotNull(documentId); - Preconditions.checkNotNull(uploadFile); - - // retrieve current user to check identity - String userFullId = getFullUserIdFromShort(userWebToken.getUserId()); - - try { - getCoselmarUserDao().forTopiaIdEquals(userFullId).findUnique(); - } catch (TopiaNoResultException tnre) { - // Should not happened, cause user are not really deleted - String message = String.format("Seems that logged user ('%s') does not exist anymore.", userFullId); - if (log.isErrorEnabled()) { - log.error(message); - } - throw new InvalidCredentialException(message); - } - - String documentFullId = getFullIdFromShort(Document.class, documentId); - Document document = getDocumentDao().forTopiaIdEquals(documentFullId).findAny(); - // Get owner to place correctly the file CoselmarUser owner = document.getOwner(); Pair<String, String> pathAndContentType = managerDocumentFile(uploadFile, owner); @@ -372,16 +364,16 @@ public class DocumentsWebService extends CoselmarWebServiceSupport { // Check authentication String authorization = getContext().getHeader("Authorization"); - UserWebToken userWebToken = checkAuthentication(authorization); + CoselmarUser currentUser = checkUserAuthentication(authorization); String documentId = getDocumentFullId(document.getId()); Document documentEntity = getDocumentDao().forTopiaIdEquals(documentId).findUnique(); - if (!isAllowedToAccessDocument(userWebToken, documentEntity)) { + if (!isAllowedToAccessDocument(currentUser, documentEntity)) { String message = String.format("User %s %s ('%s') try to modify document '%s'", - userWebToken.getFirstName(), userWebToken.getLastName(), userWebToken.getUserId(), documentId); + currentUser.getFirstname(), currentUser.getName(), getShortIdFromFull(currentUser.getTopiaId()), documentId); if (log.isWarnEnabled()) { log.warn(message); } @@ -449,17 +441,18 @@ public class DocumentsWebService extends CoselmarWebServiceSupport { // Check authentication String authorization = getContext().getHeader("Authorization"); - UserWebToken userWebToken = checkAuthentication(authorization); + CoselmarUser currentUser = checkUserAuthentication(authorization); // reconstitute full id String fullId = getDocumentFullId(documentId); Document document = getDocumentDao().forTopiaIdEquals(fullId).findUnique(); - if (!isAllowedToAccessDocument(userWebToken, document)) { + if (!DOCUMENT_EDIT_ALLOWED_USER_ROLES.contains(currentUser.getRole().name()) + && document.getOwner() != currentUser) { String message = String.format("User %s %s ('%s') try to delete document '%s'", - userWebToken.getFirstName(), userWebToken.getLastName(), userWebToken.getUserId(), documentId); + currentUser.getFirstname(), currentUser.getName(), getShortIdFromFull(currentUser.getTopiaId()), documentId); if (log.isWarnEnabled()) { log.warn(message); } @@ -559,16 +552,16 @@ public class DocumentsWebService extends CoselmarWebServiceSupport { * 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, + * @param user : 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) { + protected boolean isAllowedToAccessDocument(CoselmarUser user, Document document) { boolean isAuthorized = false; - String viewerRole = userWebToken.getRole().toUpperCase(); + String viewerRole = user.getRole().name().toUpperCase(); // For public : only admin/supervisor/expert can access if (document.getPrivacy() == Privacy.PUBLIC) { @@ -577,11 +570,17 @@ public class DocumentsWebService extends CoselmarWebServiceSupport { // For Private : only admin/supervisor/owner can access } else if (document.getPrivacy() == Privacy.PRIVATE) { CoselmarUser documentOwner = document.getOwner(); - boolean isOwner = StringUtils.equals(documentOwner.getTopiaId(), getFullUserIdFromShort(userWebToken.getUserId())); + boolean isOwner = StringUtils.equals(documentOwner.getTopiaId(), user.getTopiaId()); isAuthorized = isOwner || Lists.newArrayList(CoselmarUserRole.ADMIN.name(), CoselmarUserRole.SUPERVISOR.name()).contains(viewerRole); - } else { - // Restricted : Not yet implemented + } else if (document.getPrivacy() == Privacy.RESTRICTED) { + Set<CoselmarUserGroup> restrictedLists = document.getRestrictedList(); + for (CoselmarUserGroup restrictedList : restrictedLists) { + if (restrictedList.containsMembers(user)) { + isAuthorized = true; + break; + } + } } return isAuthorized; -- To stop receiving notification emails like this one, please contact codelutin.com SCM administrator <admin+scm@codelutin.com>.