diff --git a/spring-integration-smb/src/main/java/org/springframework/integration/smb/session/SmbFileInfo.java b/spring-integration-smb/src/main/java/org/springframework/integration/smb/session/SmbFileInfo.java index 34b146df57..5dd1ebeb67 100644 --- a/spring-integration-smb/src/main/java/org/springframework/integration/smb/session/SmbFileInfo.java +++ b/spring-integration-smb/src/main/java/org/springframework/integration/smb/session/SmbFileInfo.java @@ -17,7 +17,6 @@ package org.springframework.integration.smb.session; import java.io.IOException; -import java.util.Arrays; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -91,23 +90,70 @@ public class SmbFileInfo extends AbstractFileInfo { } /** - * An Access Control Entry (ACE) is an element in a security descriptor - * such as those associated with files and directories. The Windows OS - * determines which users have the necessary permissions to access objects - * based on these entries. - * @return a list of Access Control Entry (ACE) objects representing - * the security descriptor associated with this file or directory. + * An Access Control Entry (ACE) is an element in a security descriptor such as + * those associated with files and directories. The Windows OS determines which + * users have the necessary permissions to access objects based on these entries. + * A readable, formatted list of security descriptor entries and associated + * permissions will be returned by this implementation. + * + *
+	 * WNET\alice - Deny Write, Deny Modify, Direct - This folder only
+	 * SYSTEM - Allow Read, Allow Write, Allow Modify, Allow Execute, Allow Delete, Inherited - This folder only
+	 * WNET\alice - Allow Read, Allow Write, Allow Modify, Allow Execute, Allow Delete, Inherited - This folder only
+	 * Administrators - Allow Read, Allow Write, Allow Modify, Allow Execute, Allow Delete, Inherited - This folder only
+	 * 
+ * + * @return a list of Access Control Entry (ACE) objects representing the security + * descriptor entry and permissions associated with this file or directory. + * @see jcifs.ACE + * @see jcifs.SID */ @Override public String getPermissions() { - ACE[] aceArray = null; + ACE[] aces = null; try { - aceArray = this.smbFile.getSecurity(true); + aces = this.smbFile.getSecurity(true); } - catch (IOException se) { - logger.error("Unable to determine security descriptor information for this SmbFile", se); + catch (IOException ioe) { + logger.error("Unable to determine security descriptor information for this SmbFile", ioe); + return null; } - return (aceArray == null) ? null : Arrays.toString(aceArray); + + StringBuilder sb = new StringBuilder(); + for (ACE ace : aces) { + sb.append(ace.getSID().toDisplayString()); + sb.append(" - "); + + if ((ace.getAccessMask() & ACE.FILE_READ_DATA) != 0) { + sb.append(ace.isAllow() ? "Allow " : "Deny "); + sb.append("Read, "); + } + if ((ace.getAccessMask() & ACE.FILE_WRITE_DATA) != 0) { + sb.append(ace.isAllow() ? "Allow " : "Deny "); + sb.append("Write, "); + } + if ((ace.getAccessMask() & ACE.FILE_APPEND_DATA) != 0) { + sb.append(ace.isAllow() ? "Allow " : "Deny "); + sb.append("Modify, "); + } + if ((ace.getAccessMask() & ACE.FILE_EXECUTE) != 0) { + sb.append(ace.isAllow() ? "Allow " : "Deny "); + sb.append("Execute, "); + } + if ((ace.getAccessMask() & ACE.FILE_DELETE) != 0 + || (ace.getAccessMask() & ACE.DELETE) != 0) { + sb.append(ace.isAllow() ? "Allow " : "Deny "); + sb.append("Delete, "); + } + + sb.append(ace.isInherited() ? "Inherited - " : "Direct - "); + sb.append(ace.getApplyToText()); + sb.append("\n"); + } + logger.debug(this.getFilename()); + logger.debug("\n" + sb.toString()); + + return sb.toString(); } @Override diff --git a/spring-integration-smb/src/test/java/org/springframework/integration/smb/Main.java b/spring-integration-smb/src/test/java/org/springframework/integration/smb/Main.java deleted file mode 100644 index 2017430947..0000000000 --- a/spring-integration-smb/src/test/java/org/springframework/integration/smb/Main.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.integration.smb; - -import java.util.Scanner; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.context.support.GenericXmlApplicationContext; -import org.springframework.expression.common.LiteralExpression; -import org.springframework.integration.smb.outbound.SmbMessageHandler; -import org.springframework.integration.smb.session.SmbSessionFactory; -import org.springframework.messaging.support.GenericMessage; - -import jcifs.DialectVersion; - -/** - * Starts the Spring Context and will initialize the Spring Integration routes. - * - * @author Gunnar Hillert - * @author Artem Bilan - * @author Gregory Bragg - * - * @since 1.0 - * - */ -public final class Main { - - private static final Log LOGGER = LogFactory.getLog(Main.class); - - private Main() { } - - /** - * Load the Spring Integration Application Context - * - * @param args - command line arguments - */ - public static void main(final String... args) { - - final Scanner scanner = new Scanner(System.in); - - if (LOGGER.isInfoEnabled()) { - LOGGER.info("\n=================================================================" - + "\n " - + "\n Welcome to the Spring Integration SMB Test Client " - + "\n " - + "\n For more information please visit: " - + "\n https://github.com/SpringSource/spring-integration-extensions " - + "\n " - + "\n================================================================="); - } - - final GenericXmlApplicationContext context = new GenericXmlApplicationContext(); - - LOGGER.info("Please enter the: "); - LOGGER.info("\t- SMB Host"); - LOGGER.info("\t- SMB Share and Directory"); - LOGGER.info("\t- SMB Username"); - LOGGER.info("\t- SMB Password"); - - LOGGER.info("Host: "); - final String host = scanner.nextLine(); - - LOGGER.info("Share and Directory (e.g. myFile/path/to/): "); - final String shareAndDir = scanner.nextLine(); - - LOGGER.info("Username (e.g. guest): "); - final String username = scanner.nextLine(); - - LOGGER.info("Password (can be empty): "); - final String password = scanner.nextLine(); - - context.getEnvironment().getSystemProperties().put("host", host); - context.getEnvironment().getSystemProperties().put("shareAndDir", shareAndDir); - context.getEnvironment().getSystemProperties().put("username", username); - context.getEnvironment().getSystemProperties().put("password", password); - - context.load("classpath:META-INF/spring/integration/*-context.xml"); - context.registerShutdownHook(); - context.refresh(); - - if (LOGGER.isInfoEnabled()) { - LOGGER.info("\n=========================================================" - + "\n " - + "\n Please press 'q + Enter' to quit the application. " - + "\n " - + "\n========================================================="); - } - - SmbSessionFactory smbSessionFactory = context.getBean("smbSession", SmbSessionFactory.class); - smbSessionFactory.setSmbMinVersion(DialectVersion.SMB210); - smbSessionFactory.setSmbMaxVersion(DialectVersion.SMB311); - - LOGGER.info("Polling from Share: " + smbSessionFactory.getUrl()); - - // Create a test text file on the SMB file share - SmbMessageHandler handlerTxt = new SmbMessageHandler(smbSessionFactory); - handlerTxt.setRemoteDirectoryExpression(new LiteralExpression("remote-target-dir")); - handlerTxt.setFileNameGenerator(message -> "handlerContent.txt"); - handlerTxt.setAutoCreateDirectory(true); - handlerTxt.setUseTemporaryFileName(false); - handlerTxt.setBeanFactory(context.getBeanFactory()); - handlerTxt.afterPropertiesSet(); - handlerTxt.handleMessage(new GenericMessage("hello, my text")); - - // Create a test binary file on the SMB file share using a temporary filename - SmbMessageHandler handlerBin = new SmbMessageHandler(smbSessionFactory); - handlerBin.setRemoteDirectoryExpression(new LiteralExpression("remote-target-dir")); - handlerBin.setFileNameGenerator(message -> "handlerContent.bin"); - handlerBin.setAutoCreateDirectory(true); - handlerBin.setUseTemporaryFileName(true); - handlerBin.setBeanFactory(context.getBeanFactory()); - handlerBin.afterPropertiesSet(); - handlerBin.handleMessage(new GenericMessage("hello, my bytes".getBytes())); - - while (true) { - final String input = scanner.nextLine(); - - if ("q".equals(input.trim())) { - scanner.close(); - context.close(); - break; - } - } - - if (LOGGER.isInfoEnabled()) { - LOGGER.info("Exiting application...bye."); - } - - System.exit(0); - } - -} diff --git a/spring-integration-smb/src/test/java/org/springframework/integration/smb/dsl/SmbTests.java b/spring-integration-smb/src/test/java/org/springframework/integration/smb/dsl/SmbTests.java index f45f4a7292..3990e37b27 100644 --- a/spring-integration-smb/src/test/java/org/springframework/integration/smb/dsl/SmbTests.java +++ b/spring-integration-smb/src/test/java/org/springframework/integration/smb/dsl/SmbTests.java @@ -23,6 +23,7 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -66,6 +67,7 @@ import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; import jcifs.smb.SmbException; import jcifs.smb.SmbFile; +import jcifs.smb.SmbFileInputStream; /** * The actual SMB share must be configured in class 'SmbTestSupport' @@ -83,6 +85,10 @@ import jcifs.smb.SmbFile; * |-- subSmbSource/ * |-- subSmbSource1.txt - contains 'subSource1' * |-- subSmbSource2.txt - contains 'subSource2' + * |-- subSmbSource2/ - directory will be created in testSmbPutFlow + * |-- subSmbSource2-1.txt - file will be created in testSmbPutFlow and deleted in testSmbRmFlow + * |-- subSmbSource2-2.txt - file will be created in testSmbMputFlow + * |-- subSmbSource2-3.txt - file will be created in testSmbMputFlow and renamed in testSmbMvFlow to subSmbSource-MV-Flow-Renamed.txt * smbTarget/ * * @@ -215,7 +221,6 @@ public class SmbTests extends SmbTestSupport { @Test public void testSmbOutboundFlowWithSmbRemoteTemplate() { - SmbRemoteFileTemplate smbTemplate = new SmbRemoteFileTemplate(sessionFactory()); IntegrationFlow flow = f -> f .handle(Smb.outboundAdapter(smbTemplate) @@ -244,7 +249,6 @@ public class SmbTests extends SmbTestSupport { @Test public void testSmbOutboundFlowWithSmbRemoteTemplateAndMode() { - SmbRemoteFileTemplate smbTemplate = new SmbRemoteFileTemplate(sessionFactory()); IntegrationFlow flow = f -> f .handle(Smb.outboundAdapter(smbTemplate, FileExistsMode.APPEND) @@ -276,6 +280,36 @@ public class SmbTests extends SmbTestSupport { registration.destroy(); } + @Test + public void testSmbGetFlow() { + QueueChannel out = new QueueChannel(); + IntegrationFlow flow = f -> f + .handle( + Smb.outboundGateway(sessionFactory(), AbstractRemoteFileOutboundGateway.Command.GET, "payload") + .options(AbstractRemoteFileOutboundGateway.Option.STREAM) + .fileExistsMode(FileExistsMode.IGNORE) + .localDirectoryExpression("'" + getTargetLocalDirectoryName() + "' + #remoteDirectory") + .localFilenameExpression("#remoteFileName.replaceFirst('smbSource', 'localTarget')") + .charset(StandardCharsets.UTF_8.name()) + .useTemporaryFileName(true)) + .channel(out); + IntegrationFlowRegistration registration = this.flowContext.registration(flow).register(); + String fileName = "smbSource/subSmbSource/subSmbSource2.txt"; + registration.getInputChannel().send(new GenericMessage<>(fileName)); + Message result = out.receive(10_000); + assertThat(result).isNotNull(); + + SmbFileInputStream sfis = (SmbFileInputStream) result.getPayload(); + assertThat(sfis).isNotNull(); + + try { + sfis.close(); + } + catch (IOException ioe) { + } + registration.destroy(); + } + @Test @SuppressWarnings("unchecked") public void testSmbMgetFlow() { @@ -296,6 +330,7 @@ public class SmbTests extends SmbTestSupport { registration.getInputChannel().send(new GenericMessage<>(dir + "*")); Message result = out.receive(10_000); assertThat(result).isNotNull(); + List localFiles = (List) result.getPayload(); assertThat(localFiles.size()).as("unexpected local files " + localFiles).isEqualTo(2); @@ -329,6 +364,7 @@ public class SmbTests extends SmbTestSupport { registration.getInputChannel().send(new GenericMessage<>(dir)); Message result = out.receive(10_000); assertThat(result).isNotNull(); + List localFiles = (List) result.getPayload(); assertThat(localFiles.size()).as("unexpected local files " + localFiles).isEqualTo(2); @@ -350,7 +386,7 @@ public class SmbTests extends SmbTestSupport { IntegrationFlow flow = f -> f .handle( Smb.outboundGateway(sessionFactory(), AbstractRemoteFileOutboundGateway.Command.NLST, "payload") - .options(AbstractRemoteFileOutboundGateway.Option.ALL) + .options(AbstractRemoteFileOutboundGateway.Option.NOSORT) .fileExistsMode(FileExistsMode.IGNORE) .filterExpression("name matches 'subSmbSource|.*.txt'") .localDirectoryExpression("'" + getTargetLocalDirectoryName() + "' + #remoteDirectory") @@ -363,6 +399,7 @@ public class SmbTests extends SmbTestSupport { registration.getInputChannel().send(new GenericMessage<>(dir)); Message result = out.receive(10_000); assertThat(result).isNotNull(); + List localFilenames = (List) result.getPayload(); assertThat(localFilenames.size()).as("unexpected local filenames " + localFilenames).isEqualTo(2); @@ -373,6 +410,115 @@ public class SmbTests extends SmbTestSupport { registration.destroy(); } + @Test + public void testSmbPutFlow() { + QueueChannel out = new QueueChannel(); + IntegrationFlow flow = f -> f + .handle( + Smb.outboundGateway(sessionFactory(), AbstractRemoteFileOutboundGateway.Command.PUT, "payload") + .useTemporaryFileName(false) + .fileNameExpression("headers['" + FileHeaders.FILENAME + "']") + .remoteDirectoryExpression("'smbSource/subSmbSource2/'") + .autoCreateDirectory(true)) + .channel(out); + IntegrationFlowRegistration registration = this.flowContext.registration(flow).register(); + String fileName = "subSmbSource2-1.txt"; + Message message = MessageBuilder + .withPayload(new ByteArrayInputStream("subSmbSource2-1".getBytes(StandardCharsets.UTF_8))) + .setHeader(FileHeaders.FILENAME, fileName) + .build(); + registration.getInputChannel().send(message); + Message result = out.receive(10_000); + assertThat(result).isNotNull(); + + String path = (String) result.getPayload(); + assertThat(path).isNotNull(); + assertThat(path.contains("subSmbSource2")); + + registration.destroy(); + } + + @Test + public void testSmbRmFlow() { + QueueChannel out = new QueueChannel(); + IntegrationFlow flow = f -> f + .handle( + Smb.outboundGateway(sessionFactory(), AbstractRemoteFileOutboundGateway.Command.RM, "payload")) + .channel(out); + IntegrationFlowRegistration registration = this.flowContext.registration(flow).register(); + String fileName = "smbSource/subSmbSource2/subSmbSource2-1.txt"; + registration.getInputChannel().send(new GenericMessage<>(fileName)); + Message result = out.receive(10_000); + assertThat(result).isNotNull(); + + Boolean success = (Boolean) result.getPayload(); + assertThat(success).isTrue(); + + registration.destroy(); + } + + @Test + @SuppressWarnings("unchecked") + public void testSmbMputFlow() throws IOException { + QueueChannel out = new QueueChannel(); + IntegrationFlow flow = f -> f + .handle( + Smb.outboundGateway(sessionFactory(), AbstractRemoteFileOutboundGateway.Command.MPUT, "payload") + .options(AbstractRemoteFileOutboundGateway.Option.RECURSIVE) + .useTemporaryFileName(false) + .remoteDirectoryExpression("'smbSource/subSmbSource2/'") + .autoCreateDirectory(true) + .localDirectoryExpression("'" + getTargetLocalDirectoryName() + "' + #remoteDirectory") + .localFilenameExpression("#remoteFileName.replaceFirst('smbSource', 'localTarget')")) + .channel(out); + IntegrationFlowRegistration registration = this.flowContext.registration(flow).register(); + File file1 = new File(getTargetLocalDirectoryName(), "subSmbSource2-2.txt"); + File file2 = new File(getTargetLocalDirectoryName(), "subSmbSource2-3.txt"); + file1.createNewFile(); + file2.createNewFile(); + + List files = new ArrayList<>(); + files.add(file1); + files.add(file2); + + Message> message = MessageBuilder + .withPayload(files) + .build(); + registration.getInputChannel().send(message); + Message result = out.receive(10_000); + assertThat(result).isNotNull(); + + List remoteFilenames = (List) result.getPayload(); + assertThat(remoteFilenames).isNotNull(); + assertThat(remoteFilenames.size()).as("unexpected remote filenames " + remoteFilenames).isEqualTo(2); + + for (String filename : remoteFilenames) { + assertThat(filename.contains("subSmbSource2")); + } + + registration.destroy(); + } + + @Test + public void testSmbMvFlow() throws IOException { + QueueChannel out = new QueueChannel(); + IntegrationFlow flow = f -> f + .handle( + Smb.outboundGateway(sessionFactory(), AbstractRemoteFileOutboundGateway.Command.MV, "payload") + .renameExpression("'smbSource/subSmbSource2/subSmbSource-MV-Flow-Renamed.txt'")) + .channel(out); + IntegrationFlowRegistration registration = this.flowContext.registration(flow).register(); + String fileName = "smbSource/subSmbSource2/subSmbSource2-3.txt"; + registration.getInputChannel().send(new GenericMessage<>(fileName)); + Message result = out.receive(10_000); + assertThat(result).isNotNull(); + + Boolean success = (Boolean) result.getPayload(); + assertThat(success).isTrue(); + + registration.destroy(); + } + @Configuration @EnableIntegration @EnableIntegrationManagement diff --git a/spring-integration-smb/src/test/java/org/springframework/integration/smb/inbound/SmbInboundRemoteFileSystemSynchronizerTests.java b/spring-integration-smb/src/test/java/org/springframework/integration/smb/inbound/SmbInboundRemoteFileSystemSynchronizerTests.java index a8e9f43ca6..bfcc5b6df5 100644 --- a/spring-integration-smb/src/test/java/org/springframework/integration/smb/inbound/SmbInboundRemoteFileSystemSynchronizerTests.java +++ b/spring-integration-smb/src/test/java/org/springframework/integration/smb/inbound/SmbInboundRemoteFileSystemSynchronizerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -39,7 +39,6 @@ import jcifs.smb.SmbFile; /** * @author Markus Spann * @author Gunnar Hillert - * @since 1.0 */ public class SmbInboundRemoteFileSystemSynchronizerTests extends AbstractBaseTests { diff --git a/spring-integration-smb/src/test/resources/META-INF/spring/integration/spring-integration-context.xml b/spring-integration-smb/src/test/resources/META-INF/spring/integration/spring-integration-context.xml deleted file mode 100644 index 290203098c..0000000000 --- a/spring-integration-smb/src/test/resources/META-INF/spring/integration/spring-integration-context.xml +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - -