diff --git a/source/java/org/alfresco/repo/web/scripts/bean/ADMRemoteStore.java b/source/java/org/alfresco/repo/web/scripts/bean/ADMRemoteStore.java index 6d0052f57a..34ca406902 100644 --- a/source/java/org/alfresco/repo/web/scripts/bean/ADMRemoteStore.java +++ b/source/java/org/alfresco/repo/web/scripts/bean/ADMRemoteStore.java @@ -42,7 +42,6 @@ import org.alfresco.repo.content.MimetypeMap; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.alfresco.repo.security.permissions.AccessDeniedException; -import org.alfresco.repo.security.permissions.noop.PermissionServiceNOOPImpl; import org.alfresco.service.cmr.model.FileExistsException; import org.alfresco.service.cmr.model.FileFolderService; import org.alfresco.service.cmr.model.FileFolderUtil; @@ -55,7 +54,6 @@ import org.alfresco.service.cmr.repository.ContentService; import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.service.cmr.repository.Path; import org.alfresco.service.cmr.site.SiteInfo; import org.alfresco.service.cmr.site.SiteService; import org.alfresco.service.namespace.NamespaceService; @@ -75,8 +73,6 @@ import org.springframework.extensions.webscripts.WebScriptResponse; * in the Sites folder. Dashboard pages and component bindings are remapped to take advantage * of inherited permissions in the appropriate root site folder, ensuring that only valid * users can write to the appropriate configuration objects. - *

- * System folders are used to hide configuration from general folder browsing UI. * * @see BaseRemoteStore for the available API methods. * @@ -88,6 +84,7 @@ public class ADMRemoteStore extends BaseRemoteStore private static final String SURF_CONFIG = "surf-config"; + // patterns used to match site and user specific configuration locations private static final Pattern USER_PATTERN_1 = Pattern.compile(".*/components/.*\\.user~(.*)~.*"); private static final Pattern USER_PATTERN_2 = Pattern.compile(".*/pages/user/(.*?)(/.*)?$"); private static final Pattern SITE_PATTERN_1 = Pattern.compile(".*/components/.*\\.site~(.*)~.*"); @@ -161,10 +158,10 @@ public class ADMRemoteStore extends BaseRemoteStore protected void lastModified(final WebScriptResponse res, final String store, final String path) throws IOException { - AuthenticationUtil.runAs(new RunAsWork() + AuthenticationUtil.runAs(new RunAsWork() { @SuppressWarnings("synthetic-access") - public Object doWork() throws Exception + public Void doWork() throws Exception { final FileInfo fileInfo = resolveFilePath(path); if (fileInfo == null) @@ -195,10 +192,10 @@ public class ADMRemoteStore extends BaseRemoteStore { // TODO: could only allow appropriate users to read config docs i.e. site specific... // but currently GET requests need to run unauthenticated before user login occurs. - AuthenticationUtil.runAs(new RunAsWork() + AuthenticationUtil.runAs(new RunAsWork() { @SuppressWarnings("synthetic-access") - public Object doWork() throws Exception + public Void doWork() throws Exception { final FileInfo fileInfo = resolveFilePath(path); if (fileInfo == null || fileInfo.isFolder()) @@ -295,10 +292,10 @@ public class ADMRemoteStore extends BaseRemoteStore @Override protected void hasDocument(final WebScriptResponse res, final String store, final String path) throws IOException { - AuthenticationUtil.runAs(new RunAsWork() + AuthenticationUtil.runAs(new RunAsWork() { @SuppressWarnings("synthetic-access") - public Object doWork() throws Exception + public Void doWork() throws Exception { final FileInfo fileInfo = resolveFilePath(path); @@ -376,6 +373,7 @@ public class ADMRemoteStore extends BaseRemoteStore if (fileInfo == null || fileInfo.isFolder()) { res.setStatus(Status.STATUS_NOT_FOUND); + return; } ContentWriter writer = contentService.getWriter(fileInfo.getNodeRef(), ContentModel.PROP_CONTENT, true); @@ -400,6 +398,7 @@ public class ADMRemoteStore extends BaseRemoteStore if (fileInfo == null || fileInfo.isFolder()) { res.setStatus(Status.STATUS_NOT_FOUND); + return; } this.nodeService.deleteNode(fileInfo.getNodeRef()); @@ -423,10 +422,10 @@ public class ADMRemoteStore extends BaseRemoteStore protected void listDocuments(final WebScriptResponse res, final String store, final String path, final boolean recurse) throws IOException { - AuthenticationUtil.runAs(new RunAsWork() + AuthenticationUtil.runAs(new RunAsWork() { @SuppressWarnings("synthetic-access") - public Object doWork() throws Exception + public Void doWork() throws Exception { res.setContentType("text/plain;charset=UTF-8"); @@ -439,36 +438,7 @@ public class ADMRemoteStore extends BaseRemoteStore try { - final boolean debug = logger.isDebugEnabled(); - final Writer out = res.getWriter(); - final NodeRef surfConfigRef = aquireSurfConfigRef(path, false); - Map nameCache = new HashMap(); - List files = fileFolderService.search(fileInfo.getNodeRef(), "*", true, false, recurse); - for (final FileInfo file : files) - { - // walking up the parent tree manually until the "surf-config" parent is hit - // and manually appending the rest of the cm:name path down to the node. - StringBuilder displayPath = new StringBuilder(64); - NodeRef ref = unprotNodeService.getPrimaryParent(file.getNodeRef()).getParentRef(); - while (!ref.equals(surfConfigRef)) - { - String name = nameCache.get(ref); - if (name == null) - { - name = (String)unprotNodeService.getProperty(ref, ContentModel.PROP_NAME); - nameCache.put(ref, name); - } - displayPath.insert(0, '/'); - displayPath.insert(0, name); - ref = unprotNodeService.getPrimaryParent(ref).getParentRef(); - } - - out.write("/alfresco/site-data/"); - out.write(displayPath.toString()); - out.write(file.getName()); - out.write('\n'); - if (debug) logger.debug(" /alfresco/site-data/" + displayPath.toString() + "/" + file.getName()); - } + outputFileNodes(res.getWriter(), fileInfo, aquireSurfConfigRef(path, false), "*", recurse); } catch (AccessDeniedException ae) { @@ -499,10 +469,10 @@ public class ADMRemoteStore extends BaseRemoteStore protected void listDocuments(final WebScriptResponse res, final String store, final String path, final String pattern) throws IOException { - AuthenticationUtil.runAs(new RunAsWork() + AuthenticationUtil.runAs(new RunAsWork() { @SuppressWarnings("synthetic-access") - public Object doWork() throws Exception + public Void doWork() throws Exception { res.setContentType("text/plain;charset=UTF-8"); @@ -519,48 +489,21 @@ public class ADMRemoteStore extends BaseRemoteStore filePattern = "*"; } - final boolean debug = logger.isDebugEnabled(); + if (logger.isDebugEnabled()) + logger.debug("listDocuments() pattern: " + pattern); - // - // TODO: remove use of SearchService here! Jan wrote a DB version for AVM - same for DM required. - // - - // encode the qname match - and convert back any wildcard characters - /*String qname = ISO9075.encode(filePattern); - qname = qname.replace(ISO9075_ASTERIX, "*"); - - final String encPath = toQNamePath(path); - final StringBuilder query = new StringBuilder(128); - query.append("+PATH:\"/").append(NamespaceService.APP_MODEL_PREFIX).append(":").append(ADMROOT) - .append(encPath != null && encPath.length() != 0 ? encPath : "") - .append("//*\" +QNAME:") - .append(qname); - - final Writer out = res.getWriter(); - final int cropPoint = rootPath.length() + 1; - StoreRef storeRef = new StoreRef(StoreRef.PROTOCOL_WORKSPACE, store); - String sQuery = query.toString(); - ResultSet resultSet = searchService.query(storeRef, SearchService.LANGUAGE_LUCENE, sQuery); try { - List nodes = resultSet.getNodeRefs(); - for (final NodeRef nodeRef : nodes) - { - String name = (String)unprotNodeService.getProperty(nodeRef, ContentModel.PROP_NAME); - Path path = unprotNodeService.getPath(nodeRef); - // TODO: optimize this section - String displayPath = path.toDisplayPath(unprotNodeService, new PermissionServiceNOOPImpl()); - out.write(displayPath.substring(cropPoint)); - out.write('/'); - out.write(name); - out.write('\n'); - } + outputFileNodes(res.getWriter(), fileInfo, aquireSurfConfigRef(path, false), pattern, false); + } + catch (AccessDeniedException ae) + { + res.setStatus(Status.STATUS_UNAUTHORIZED); } finally { - resultSet.close(); - }*/ - // TODO: close writer! + res.getWriter().close(); + } return null; } }, AuthenticationUtil.getSystemUserName()); @@ -600,7 +543,7 @@ public class ADMRemoteStore extends BaseRemoteStore // break down the path into its component elements List pathElements = new ArrayList(4); final StringTokenizer t = new StringTokenizer(encodePath(path), "/"); - // we require paths of the form /alfresco/site-data/[/]/.xml + // the store requires paths of the form /alfresco/site-data/[/]/.xml if (t.countTokens() >= 3) { t.nextToken(); // skip /alfresco @@ -676,7 +619,7 @@ public class ADMRemoteStore extends BaseRemoteStore siteName = matcher.group(1); } - NodeRef surfConfigRef; + NodeRef surfConfigRef = null; if (userId != null) { if (logger.isDebugEnabled()) @@ -687,7 +630,11 @@ public class ADMRemoteStore extends BaseRemoteStore { if (logger.isDebugEnabled()) logger.debug("...resolved site path id: " + siteName); - surfConfigRef = getSurfConfigNodeRef(getSiteNodeRef(siteName), create); + NodeRef siteRef = getSiteNodeRef(siteName); + if (siteRef != null) + { + surfConfigRef = getSurfConfigNodeRef(siteRef, create); + } } else { @@ -726,19 +673,21 @@ public class ADMRemoteStore extends BaseRemoteStore NodeRef surfConfigRef = this.unprotNodeService.getChildByName( rootRef, ContentModel.ASSOC_CONTAINS, SURF_CONFIG); // - // TODO: protect with RRW - keyed from rootRef? + // TODO: Does this need protecting with RRW - keyed from rootRef? + // As given the use cases, only the surf-config folder in Sites has the potential + // for multi-user WRITE access e.g. 2 users creating their dashboards at same time + // - which could be solved by creating the surf-config folder as part of the patch code + // and on bootstrap for new repo also. + // As the site level surf-config folders will be created by which ever user creates the + // site - no READ access will be possible until then anyway... // - if (surfConfigRef == null && create) + if (create && surfConfigRef == null) { if (logger.isDebugEnabled()) - logger.debug("'surf-config' system folder not found under path, creating..."); + logger.debug("'surf-config' folder not found under path, creating..."); QName assocQName = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, SURF_CONFIG); Map properties = new HashMap(1, 1.0f); properties.put(ContentModel.PROP_NAME, (Serializable) SURF_CONFIG); - // - // TODO: change this to TYPE_SYSTEMFOLDER before commit? that won't stop config files getting found... - // suggest new model type that is not cm:content - and is NOT fts enabled... - // ChildAssociationRef ref = this.unprotNodeService.createNode( rootRef, ContentModel.ASSOC_CONTAINS, assocQName, ContentModel.TYPE_FOLDER, properties); surfConfigRef = ref.getChildRef(); @@ -766,30 +715,46 @@ public class ADMRemoteStore extends BaseRemoteStore } /** - * Traverse a Node and recursively output the file paths it contains. + * Output the matching file paths a node contains based on a pattern search. * * @param out Writer for output - relative paths separated by newline characters - * @param node The FileInfo node to traverse + * @param surfConfigRef Surf-Config folder + * @param fileInfo The FileInfo node to use as the parent * @param pattern Optional pattern to match filenames against - * @param recurse True to recurse sub-directories + * @param recurse True to recurse sub-directories * * @throws IOException */ - private void traverseNode(Writer out, FileInfo node, String pattern, boolean recurse) + private void outputFileNodes(Writer out, FileInfo fileInfo, NodeRef surfConfigRef, String pattern, boolean recurse) throws IOException { - final int cropPoint = 0;//TODO:rootPath.length() + 1; - - // TODO: this could be optimized a lot: - // 1. Perform the traverse and search by hand (is this faster?) using small nodeService - // 2. Keep track of parent path cm:name values so Path is not required - List files = this.fileFolderService.search(getRootNodeRef(), pattern, true, false, recurse); + final boolean debug = logger.isDebugEnabled(); + final Map nameCache = new HashMap(); + final List files = fileFolderService.search(fileInfo.getNodeRef(), pattern, true, false, recurse); for (final FileInfo file : files) { - Path path = this.unprotNodeService.getPath(file.getNodeRef()); - String displayPath = path.toDisplayPath(this.unprotNodeService, new PermissionServiceNOOPImpl()); - out.write(displayPath.substring(cropPoint)); - out.write("\n"); + // walking up the parent tree manually until the "surf-config" parent is hit + // and manually appending the rest of the cm:name path down to the node. + StringBuilder displayPath = new StringBuilder(64); + NodeRef ref = unprotNodeService.getPrimaryParent(file.getNodeRef()).getParentRef(); + while (!ref.equals(surfConfigRef)) + { + String name = nameCache.get(ref); + if (name == null) + { + name = (String)unprotNodeService.getProperty(ref, ContentModel.PROP_NAME); + nameCache.put(ref, name); + } + displayPath.insert(0, '/'); + displayPath.insert(0, name); + ref = unprotNodeService.getPrimaryParent(ref).getParentRef(); + } + + out.write("/alfresco/site-data/"); + out.write(displayPath.toString()); + out.write(file.getName()); + out.write('\n'); + if (debug) logger.debug(" /alfresco/site-data/" + displayPath.toString() + file.getName()); } } } diff --git a/source/java/org/alfresco/repo/web/scripts/bean/AVMRemoteStore.java b/source/java/org/alfresco/repo/web/scripts/bean/AVMRemoteStore.java index c3864776cc..032d483688 100644 --- a/source/java/org/alfresco/repo/web/scripts/bean/AVMRemoteStore.java +++ b/source/java/org/alfresco/repo/web/scripts/bean/AVMRemoteStore.java @@ -159,10 +159,10 @@ public class AVMRemoteStore extends BaseRemoteStore return; } - AuthenticationUtil.runAs(new RunAsWork() + AuthenticationUtil.runAs(new RunAsWork() { @SuppressWarnings("synthetic-access") - public Object doWork() throws Exception + public Void doWork() throws Exception { ContentReader reader; try @@ -251,10 +251,10 @@ public class AVMRemoteStore extends BaseRemoteStore @Override protected void createDocument(final WebScriptResponse res, final String store, final String path, final InputStream content) { - AuthenticationUtil.runAs(new RunAsWork() + AuthenticationUtil.runAs(new RunAsWork() { @SuppressWarnings("synthetic-access") - public Object doWork() throws Exception + public Void doWork() throws Exception { String avmPath = buildAVMPath(store, path); try @@ -298,10 +298,10 @@ public class AVMRemoteStore extends BaseRemoteStore @Override protected void createDocuments(final WebScriptResponse res, final String store, final InputStream in) { - AuthenticationUtil.runAs(new RunAsWork() + AuthenticationUtil.runAs(new RunAsWork() { @SuppressWarnings("synthetic-access") - public Object doWork() throws Exception + public Void doWork() throws Exception { try { @@ -384,10 +384,10 @@ public class AVMRemoteStore extends BaseRemoteStore return; } - AuthenticationUtil.runAs(new RunAsWork() + AuthenticationUtil.runAs(new RunAsWork() { @SuppressWarnings("synthetic-access") - public Object doWork() throws Exception + public Void doWork() throws Exception { try { @@ -420,10 +420,10 @@ public class AVMRemoteStore extends BaseRemoteStore return; } - AuthenticationUtil.runAs(new RunAsWork() + AuthenticationUtil.runAs(new RunAsWork() { @SuppressWarnings("synthetic-access") - public Object doWork() throws Exception + public Void doWork() throws Exception { try {