@Override public void constraintRenditionStreamDownload(Content content, String streamId) { List<String> renditions = content.getRenditionIds(); if (CollectionUtils.isEmpty(renditions) || !renditions.contains(streamId)) { constraint(content.getId(), "This document has no rendition specified with " + streamId); } }
/** Filtering check to a list of contents based on the permission */ @Override public <T> List<T> getFiltered(CallContext callContext, String repositoryId, List<T> contents) { List<T> result = new ArrayList<T>(); // Validation // TODO refine the logic if (CollectionUtils.isEmpty(contents)) { return null; } // Filtering for (T _content : contents) { Content content = (Content) _content; Acl acl = contentService.calculateAcl(repositoryId, content); Boolean filtered = checkPermission( callContext, repositoryId, PermissionMapping.CAN_GET_PROPERTIES_OBJECT, acl, content.getType(), content); if (filtered) { result.add(_content); } } return result; }
private Content checkExceptionBeforeUpdateProperties( CallContext callContext, String repositoryId, Holder<String> objectId, Properties properties, Holder<String> changeToken) { // ////////////////// // General Exception // ////////////////// exceptionService.invalidArgumentRequiredCollection("properties", properties.getPropertyList()); Content content = contentService.getContent(repositoryId, objectId.getValue()); exceptionService.objectNotFound(DomainType.OBJECT, content, objectId.getValue()); if (content.isDocument()) { Document d = (Document) content; exceptionService.versioning(d); exceptionService.constraintUpdateWhenCheckedOut(repositoryId, callContext.getUsername(), d); TypeDefinition typeDef = typeManager.getTypeDefinition(repositoryId, d); exceptionService.constraintImmutable(repositoryId, d, typeDef); } exceptionService.permissionDenied( callContext, repositoryId, PermissionMapping.CAN_UPDATE_PROPERTIES_OBJECT, content); exceptionService.updateConflict(content, changeToken); TypeDefinition tdf = typeManager.getTypeDefinition(repositoryId, content); exceptionService.constraintPropertyValue(repositoryId, tdf, properties, objectId.getValue()); return content; }
@Override public BulkUpdateObjectIdAndChangeToken call() throws Exception { exceptionService.invalidArgumentRequiredString("objectId", objectIdAndChangeToken.getId()); Lock lock = threadLockService.getWriteLock(repositoryId, objectIdAndChangeToken.getId()); try { lock.lock(); Content content = checkExceptionBeforeUpdateProperties( callContext, repositoryId, new Holder<String>(objectIdAndChangeToken.getId()), properties, new Holder<String>(objectIdAndChangeToken.getChangeToken())); contentService.updateProperties(callContext, repositoryId, properties, content); nemakiCachePool.get(repositoryId).removeCmisCache(content.getId()); BulkUpdateObjectIdAndChangeToken result = new BulkUpdateObjectIdAndChangeTokenImpl( objectIdAndChangeToken.getId(), content.getId(), String.valueOf(content.getChangeToken())); return result; } catch (Exception e) { // Don't throw an error // Don't return any BulkUpdateObjectIdAndChangetoken } finally { lock.unlock(); } // TODO Auto-generated method stub return null; }
@Override public void constraintAllowedTargetTypes( RelationshipTypeDefinition relationshipTypeDefinition, Content target) { List<String> allowed = relationshipTypeDefinition.getAllowedTargetTypeIds(); if (CollectionUtils.isNotEmpty(allowed)) { if (!allowed.contains(target.getObjectType())) constraint(target.getId(), "The target object's type is not allowed for the relationship"); } }
private void permissionTopLevelFolder( CallContext context, String repositoryId, String key, Content content) { boolean result = permissionService.checkPermissionAtTopLevel(context, repositoryId, key, content); if (!result) { String msg = String.format( "Permission Denied to top level folders for non-admin user! repositoryId=%s key=%s userId=%s content={id:%s, name:%s} ", repositoryId, key, context.getUsername(), content.getId(), content.getName()); throw new CmisPermissionDeniedException(msg, HTTP_STATUS_CODE_403); } }
private void permissionDeniedInternal( CallContext callContext, String repositoryId, String key, Acl acl, String baseTypeId, Content content) { if (!permissionService.checkPermission( callContext, repositoryId, key, acl, baseTypeId, content)) { String msg = String.format( "Permission Denied! repositoryId=%s key=%s acl=%s content={id:%s, name:%s} ", repositoryId, key, acl, content.getId(), content.getName()); throw new CmisPermissionDeniedException(msg, HTTP_STATUS_CODE_403); } }
@Override public ObjectData getObjectByPath( CallContext callContext, String repositoryId, String path, String filter, Boolean includeAllowableActions, IncludeRelationships includeRelationships, String renditionFilter, Boolean includePolicyIds, Boolean includeAcl, ExtensionsData extension) { // ////////////////// // General Exception // ////////////////// exceptionService.invalidArgumentRequired("objectId", path); // FIXME path is not preserved in db. Content content = contentService.getContentByPath(repositoryId, path); // TODO create objectNotFoundByPath method exceptionService.objectNotFoundByPath(DomainType.OBJECT, content, path); Lock lock = threadLockService.getReadLock(repositoryId, content.getId()); try { lock.lock(); exceptionService.permissionDenied( callContext, repositoryId, PermissionMapping.CAN_GET_PROPERTIES_OBJECT, content); // ////////////////// // Body of the method // ////////////////// return compileService.compileObjectData( callContext, repositoryId, content, filter, includeAllowableActions, includeRelationships, renditionFilter, includeAcl); } finally { lock.unlock(); } }
private ContentStream getRenditionStream(String repositoryId, Content content, String streamId) { if (!content.isDocument() && !content.isFolder()) { exceptionService.constraint( content.getId(), "getRenditionStream cannnot be invoked to other than document or folder type."); } exceptionService.constraintRenditionStreamDownload(content, streamId); Rendition rendition = contentService.getRendition(repositoryId, streamId); BigInteger length = BigInteger.valueOf(rendition.getLength()); String mimeType = rendition.getMimetype(); InputStream is = rendition.getInputStream(); ContentStream cs = new ContentStreamImpl("preview_" + streamId, length, mimeType, is); return cs; }
@Override public void updateConflict(Content content, Holder<String> changeToken) { if ((changeToken == null || changeToken.getValue() == null)) { throw new CmisUpdateConflictException( "Change token is required to update", HTTP_STATUS_CODE_409); } else if (!changeToken.getValue().equals(content.getChangeToken())) { throw new CmisUpdateConflictException( "Cannot update because the changeToken conflicts", HTTP_STATUS_CODE_409); } }
public void execute() { if (content.isDocument()) { Future<Boolean> result = parentService .getService() .submit(new DeleteTask(callContext, repositoryId, content, allVersions)); failureIds.put(content.getId(), result); } else if (content.isFolder()) { WrappedExecutorService childrenService = new WrappedExecutorService(Executors.newFixedThreadPool(threadMax), (Folder) content); List<Content> children = contentService.getChildren(repositoryId, content.getId()); if (CollectionUtils.isNotEmpty(children)) { for (Content child : children) { DeleteService deleteService = new DeleteService( this.failureIds, childrenService, callContext, repositoryId, child, allVersions); deleteService.execute(); } } // wait til newService ends childrenService.getService().shutdown(); try { childrenService.getService().awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS); } catch (InterruptedException e) { log.error(e, e); } // Lastly, delete self Future<Boolean> result = parentService .getService() .submit(new DeleteTask(callContext, repositoryId, content, allVersions)); failureIds.put(content.getId(), result); } }
@Override public void moveObject( CallContext callContext, String repositoryId, Holder<String> objectId, String sourceFolderId, String targetFolderId) { exceptionService.invalidArgumentRequiredHolderString("objectId", objectId); Lock lock = threadLockService.getWriteLock(repositoryId, objectId.getValue()); try { lock.lock(); // ////////////////// // General Exception // ////////////////// exceptionService.invalidArgumentRequiredString("sourceFolderId", sourceFolderId); exceptionService.invalidArgumentRequiredString("targetFolderId", targetFolderId); Content content = contentService.getContent(repositoryId, objectId.getValue()); exceptionService.objectNotFound(DomainType.OBJECT, content, objectId.getValue()); Folder source = contentService.getFolder(repositoryId, sourceFolderId); exceptionService.objectNotFound(DomainType.OBJECT, source, sourceFolderId); Folder target = contentService.getFolder(repositoryId, targetFolderId); exceptionService.objectNotFound(DomainType.OBJECT, target, targetFolderId); exceptionService.permissionDenied( callContext, repositoryId, PermissionMapping.CAN_MOVE_OBJECT, content); exceptionService.permissionDenied( callContext, repositoryId, PermissionMapping.CAN_MOVE_SOURCE, source); exceptionService.permissionDenied( callContext, repositoryId, PermissionMapping.CAN_MOVE_TARGET, target); // ////////////////// // Body of the method // ////////////////// contentService.move(callContext, repositoryId, content, target); nemakiCachePool.get(repositoryId).removeCmisCache(content.getId()); } finally { lock.unlock(); } }
// TODO Implement HTTP range(offset and length of stream), though it is not // obligatory. private ContentStream getContentStreamInternal( String repositoryId, Content content, BigInteger rangeOffset, BigInteger rangeLength) { if (!content.isDocument()) { exceptionService.constraint( content.getId(), "getContentStream cannnot be invoked to other than document type."); } Document document = (Document) content; exceptionService.constraintContentStreamDownload(repositoryId, document); AttachmentNode attachment = contentService.getAttachment(repositoryId, document.getAttachmentNodeId()); attachment.setRangeOffset(rangeOffset); attachment.setRangeLength(rangeLength); // Set content stream BigInteger length = BigInteger.valueOf(attachment.getLength()); String name = attachment.getName(); String mimeType = attachment.getMimeType(); InputStream is = attachment.getInputStream(); ContentStream cs = new ContentStreamImpl(name, length, mimeType, is); return cs; }
// TODO Show also stack errors @Override public void permissionDenied( CallContext context, String repositoryId, String key, Content content) { if (content == null) { System.out.println(); } String baseTypeId = content.getType(); Acl acl = contentService.calculateAcl(repositoryId, content); permissionDeniedInternal(context, repositoryId, key, acl, baseTypeId, content); permissionTopLevelFolder(context, repositoryId, key, content); }
@Override public void contentAlreadyExists(Content content, Boolean overwriteFlag) { if (!overwriteFlag) { Document document = (Document) content; // FIXME String attachmentNodeId = document.getAttachmentNodeId(); // FIXME // getAttachmentNodes if (attachmentNodeId != null) { String msg = "Can't overwrite the content stream when overwriteFlag is false"; throw new CmisContentAlreadyExistsException( buildMsgWithId(msg, content.getId()), HTTP_STATUS_CODE_409); } } }
/** * TODO In the future, enable different configuration for Read/Update/Delete. * * @param callContext * @param repositoryId TODO * @param key * @param relationship * @return */ private Boolean checkRelationshipPermission( CallContext callContext, String repositoryId, String key, Relationship relationship) { Content source = contentService.getRelationship(repositoryId, relationship.getSourceId()); Content target = contentService.getRelationship(repositoryId, relationship.getTargetId()); if (source == null || target == null) { log.warn( "[objectId=" + relationship.getId() + "]Source or target of this relationship is missing"); return false; } // Read action when a relationship is specified directly if (PermissionMapping.CAN_GET_PROPERTIES_OBJECT.equals(key)) { boolean readSource = checkPermission( callContext, repositoryId, PermissionMapping.CAN_GET_OBJECT_RELATIONSHIPS_OBJECT, contentService.calculateAcl(repositoryId, source), source.getType(), source); boolean readTarget = checkPermission( callContext, repositoryId, PermissionMapping.CAN_GET_OBJECT_RELATIONSHIPS_OBJECT, contentService.calculateAcl(repositoryId, target), target.getType(), target); return readSource | readTarget; } // Update action if (PermissionMapping.CAN_UPDATE_PROPERTIES_OBJECT.equals(key)) { boolean updateSource = checkPermission( callContext, repositoryId, PermissionMapping.CAN_GET_OBJECT_RELATIONSHIPS_OBJECT, contentService.calculateAcl(repositoryId, source), source.getType(), source); boolean updateTarget = checkPermission( callContext, repositoryId, PermissionMapping.CAN_GET_OBJECT_RELATIONSHIPS_OBJECT, contentService.calculateAcl(repositoryId, target), target.getType(), target); return updateSource | updateTarget; } // Delete action if (PermissionMapping.CAN_DELETE_OBJECT.equals(key)) { boolean deleteSource = checkPermission( callContext, repositoryId, PermissionMapping.CAN_GET_OBJECT_RELATIONSHIPS_OBJECT, contentService.calculateAcl(repositoryId, source), source.getType(), source); boolean deleteTarget = checkPermission( callContext, repositoryId, PermissionMapping.CAN_GET_OBJECT_RELATIONSHIPS_OBJECT, contentService.calculateAcl(repositoryId, target), target.getType(), target); return deleteSource | deleteTarget; } return false; }
// TODO Merge arguments(acl, content) // FIXME Refactor duplicate isAllowableBaseType @Override public Boolean checkPermission( CallContext callContext, String repositoryId, String key, Acl acl, String baseType, Content content) { // All permission checks must go through baseType check if (!isAllowableBaseType(key, baseType, content, repositoryId)) return false; // Admin always pass a permission check CallContextImpl cci = (CallContextImpl) callContext; Boolean _isAdmin = (Boolean) cci.get(CallContextKey.IS_ADMIN); boolean isAdmin = (_isAdmin == null) ? false : _isAdmin; if (isAdmin) return true; // PWC doesn't accept any actions from a non-owner user // TODO admin can manipulate PWC even when it is checked out ? if (content.isDocument()) { Document document = (Document) content; if (document.isPrivateWorkingCopy()) { VersionSeries vs = contentService.getVersionSeries(repositoryId, document); if (!callContext.getUsername().equals(vs.getVersionSeriesCheckedOutBy())) { return false; } } } // Relation has no ACL stored in DB. // Though some actions are defined in the specs, // Some other direct actions is needed to be set here. if (content.isRelationship()) { Relationship relationship = (Relationship) content; return checkRelationshipPermission(callContext, repositoryId, key, relationship); } // Void Acl fails(but Admin can do an action) if (acl == null) return false; // Even if a user has multiple ACEs, the permissions is pushed into // Set<String> and remain unique. // Get ACL for the current user String userName = callContext.getUsername(); List<Ace> aces = acl.getAllAces(); Set<String> userPermissions = new HashSet<String>(); Set<String> groups = principalService.getGroupIdsContainingUser(repositoryId, userName); for (Ace ace : aces) { // Filter ace which has not permissions if (ace.getPermissions() == null) continue; // Add user permissions if (ace.getPrincipalId().equals(userName)) { userPermissions.addAll(ace.getPermissions()); } // Add inherited permissions which user inherits if (CollectionUtils.isNotEmpty(groups) && groups.contains(ace.getPrincipalId())) { userPermissions.addAll(ace.getPermissions()); } } // Check mapping between the user and the content return checkCalculatedPermissions(repositoryId, key, userPermissions); }