Skip to content

Commit

Permalink
Merge pull request #16668 from iterate-ch/bugfix/GH-16667
Browse files Browse the repository at this point in the history
Add preflight checks.
  • Loading branch information
dkocher authored Dec 14, 2024
2 parents fffbec7 + 2618d46 commit 15a2cb9
Show file tree
Hide file tree
Showing 37 changed files with 342 additions and 110 deletions.
3 changes: 3 additions & 0 deletions box/src/main/java/ch/cyberduck/core/box/BoxCopyFeature.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
import java.util.Collections;
import java.util.EnumSet;

import static ch.cyberduck.core.features.Copy.validate;

public class BoxCopyFeature implements Copy {
private static final Logger log = LogManager.getLogger(BoxCopyFeature.class);

Expand Down Expand Up @@ -89,5 +91,6 @@ public void preflight(final Path source, final Path target) throws BackgroundExc
if(!BoxTouchFeature.validate(target.getName())) {
throw new InvalidFilenameException(MessageFormat.format(LocaleFactory.localizedString("Cannot create {0}", "Error"), target.getName()));
}
validate(session.getCaseSensitivity(), source, target);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@
*/

import ch.cyberduck.core.CachingFileIdProvider;
import ch.cyberduck.core.CaseInsensitivePathPredicate;
import ch.cyberduck.core.DisabledListProgressListener;
import ch.cyberduck.core.Path;
import ch.cyberduck.core.SimplePathPredicate;
import ch.cyberduck.core.exception.BackgroundException;
import ch.cyberduck.core.exception.NotfoundException;
import ch.cyberduck.core.features.FileIdProvider;
Expand Down Expand Up @@ -53,7 +53,7 @@ public String getFileId(final Path file) throws BackgroundException {
return ROOT;
}
final Path f = new BoxListService(session, this).list(file.getParent(),
new DisabledListProgressListener()).find(new SimplePathPredicate(file));
new DisabledListProgressListener()).find(new CaseInsensitivePathPredicate(file));
if(null == f) {
throw new NotfoundException(file.getAbsolute());
}
Expand Down
8 changes: 5 additions & 3 deletions box/src/main/java/ch/cyberduck/core/box/BoxMoveFeature.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,10 @@ public BoxMoveFeature(final BoxSession session, final BoxFileidProvider fileid)
public Path move(final Path file, final Path renamed, final TransferStatus status, final Delete.Callback delete, final ConnectionCallback callback) throws BackgroundException {
try {
if(status.isExists()) {
log.warn("Delete file {} to be replaced with {}", renamed, file);
new BoxDeleteFeature(session, fileid).delete(Collections.singletonList(renamed), callback, delete);
if(!fileid.getFileId(file).equals(fileid.getFileId(renamed))) {
log.warn("Delete file {} to be replaced with {}", renamed, file);
new BoxDeleteFeature(session, fileid).delete(Collections.singletonList(renamed), callback, delete);
}
}
final String id = fileid.getFileId(file);
if(file.isDirectory()) {
Expand Down Expand Up @@ -93,7 +95,7 @@ public EnumSet<Flags> features(final Path source, final Path target) {
@Override
public void preflight(final Path source, final Path target) throws BackgroundException {
if(!BoxTouchFeature.validate(target.getName())) {
throw new InvalidFilenameException(MessageFormat.format(LocaleFactory.localizedString("Cannot create {0}", "Error"), target.getName()));
throw new InvalidFilenameException(MessageFormat.format(LocaleFactory.localizedString("Cannot create {0}", "Error"), target.getName())).withFile(source);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,24 @@
* GNU General Public License for more details.
*/

import ch.cyberduck.core.AlphanumericRandomStringService;
import ch.cyberduck.core.DisabledLoginCallback;
import ch.cyberduck.core.Path;
import ch.cyberduck.core.PathAttributes;
import ch.cyberduck.core.exception.NotfoundException;
import ch.cyberduck.core.features.Delete;
import ch.cyberduck.core.shared.DefaultHomeFinderService;
import ch.cyberduck.core.transfer.TransferStatus;
import ch.cyberduck.test.IntegrationTest;

import org.apache.commons.lang3.StringUtils;
import org.junit.Test;
import org.junit.experimental.categories.Category;

import java.util.Collections;
import java.util.EnumSet;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.*;

@Category(IntegrationTest.class)
public class BoxFileidProviderTest extends AbstractBoxTest {
Expand All @@ -33,4 +42,29 @@ public void getFileIdRoot() throws Exception {
assertEquals(BoxFileidProvider.ROOT, new BoxFileidProvider(session).getFileId(
new Path("/", EnumSet.of(Path.Type.directory))));
}

@Test
public void getFileIdFile() throws Exception {
final BoxFileidProvider nodeid = new BoxFileidProvider(session);
final Path home = new DefaultHomeFinderService(session).find();
final String name = String.format("%s%s", new AlphanumericRandomStringService().random(), new AlphanumericRandomStringService().random());
final Path file = new BoxTouchFeature(session, nodeid).touch(new Path(home, name, EnumSet.of(Path.Type.file)), new TransferStatus());
nodeid.clear();
final String nodeId = nodeid.getFileId(new Path(home, name, EnumSet.of(Path.Type.file)));
assertNotNull(nodeId);
nodeid.clear();
assertEquals(nodeId, nodeid.getFileId(new Path(home.withAttributes(PathAttributes.EMPTY), name, EnumSet.of(Path.Type.file))));
nodeid.clear();
assertEquals(nodeId, nodeid.getFileId(new Path(home, StringUtils.upperCase(name), EnumSet.of(Path.Type.file))));
nodeid.clear();
assertEquals(nodeId, nodeid.getFileId(new Path(home, StringUtils.lowerCase(name), EnumSet.of(Path.Type.file))));
try {
assertNull(nodeid.getFileId(new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file))));
fail();
}
catch(NotfoundException e) {
// Expected
}
new BoxDeleteFeature(session, nodeid).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback());
}
}
11 changes: 11 additions & 0 deletions box/src/test/java/ch/cyberduck/core/box/BoxMoveFeatureTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import ch.cyberduck.core.transfer.TransferStatus;
import ch.cyberduck.test.IntegrationTest;

import org.apache.commons.lang3.StringUtils;
import org.junit.Test;
import org.junit.experimental.categories.Category;

Expand Down Expand Up @@ -90,4 +91,14 @@ public void testMoveNotFound() throws Exception {
final Path test = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
new BoxMoveFeature(session, fileid).move(test, new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus(), new Delete.DisabledCallback(), new DisabledConnectionCallback());
}

@Test
public void testRenameCaseOnly() throws Exception {
final BoxFileidProvider fileid = new BoxFileidProvider(session);
final String name = new AlphanumericRandomStringService().random();
final Path file = new BoxTouchFeature(session, fileid).touch(new Path(new DefaultHomeFinderService(session).find(), StringUtils.capitalize(name), EnumSet.of(Path.Type.file)), new TransferStatus());
final Path rename = new Path(new DefaultHomeFinderService(session).find(), StringUtils.lowerCase(name), EnumSet.of(Path.Type.file));
new BoxMoveFeature(session, fileid).move(file, rename, new TransferStatus().exists(true), new Delete.DisabledCallback(), new DisabledConnectionCallback());
new BoxDeleteFeature(session, fileid).delete(Collections.singletonList(rename), new DisabledLoginCallback(), new Delete.DisabledCallback());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
import java.util.Collections;
import java.util.EnumSet;

import static ch.cyberduck.core.features.Copy.validate;

public class BrickCopyFeature extends BrickFileMigrationFeature implements Copy {
private static final Logger log = LogManager.getLogger(BrickCopyFeature.class);

Expand Down Expand Up @@ -69,4 +71,10 @@ public Path copy(final Path file, final Path target, final TransferStatus status
public EnumSet<Flags> features(final Path source, final Path target) {
return EnumSet.of(Flags.recursive);
}

@Override
public void preflight(final Path source, final Path target) throws BackgroundException {
Copy.super.preflight(source, target);
validate(session.getCaseSensitivity(), source, target);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,17 @@
* GNU General Public License for more details.
*/

import ch.cyberduck.core.unicode.NFCNormalizer;
import ch.cyberduck.core.unicode.UnicodeNormalizer;

import org.apache.commons.lang3.StringUtils;

public final class CaseInsensitivePathPredicate extends SimplePathPredicate {

private static final UnicodeNormalizer normalizer = new NFCNormalizer();
public class CaseInsensitivePathPredicate extends SimplePathPredicate {

public CaseInsensitivePathPredicate(final Path file) {
super(file.isSymbolicLink() ? Path.Type.symboliclink : file.isFile() ? Path.Type.file : Path.Type.directory,
StringUtils.lowerCase(normalizer.normalize(file.getAbsolute()).toString()));
StringUtils.lowerCase(file.getAbsolute()));
}

public CaseInsensitivePathPredicate(final Path.Type type, final String path) {
super(type, StringUtils.lowerCase(path));
}

@Override
Expand Down
27 changes: 27 additions & 0 deletions core/src/main/java/ch/cyberduck/core/features/Copy.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,13 @@
* GNU General Public License for more details.
*/

import ch.cyberduck.core.CaseInsensitivePathPredicate;
import ch.cyberduck.core.ConnectionCallback;
import ch.cyberduck.core.LocaleFactory;
import ch.cyberduck.core.Path;
import ch.cyberduck.core.Protocol;
import ch.cyberduck.core.Session;
import ch.cyberduck.core.SimplePathPredicate;
import ch.cyberduck.core.exception.AccessDeniedException;
import ch.cyberduck.core.exception.BackgroundException;
import ch.cyberduck.core.exception.InvalidFilenameException;
Expand Down Expand Up @@ -86,8 +89,32 @@ default void preflight(final Path source, final Path target) throws BackgroundEx
throw new UnsupportedException(MessageFormat.format(LocaleFactory.localizedString("Cannot copy {0}", "Error"),
source.getName())).withFile(source);
}
validate(Protocol.Case.sensitive, source, target);
}

/**
* Fail when source and target only differ with change in case of filename and protocol is case-insensitive
*
* @throws UnsupportedException Copying file to same location is not supported
*/
static void validate(final Protocol.Case sensitivity, final Path source, final Path target) throws BackgroundException {
switch(sensitivity) {
case insensitive:
if(new CaseInsensitivePathPredicate(source).test(target)) {
throw new UnsupportedException(MessageFormat.format(LocaleFactory.localizedString("Cannot copy {0}", "Error"),
source.getName())).withFile(source);
}
break;
case sensitive:
if(new SimplePathPredicate(source).test(target)) {
throw new AccessDeniedException(MessageFormat.format(LocaleFactory.localizedString("Cannot copy {0}", "Error"),
source.getName())).withFile(source);
}
break;
}
}


/**
* @return Supported features
*/
Expand Down
13 changes: 9 additions & 4 deletions core/src/main/java/ch/cyberduck/core/features/Move.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import ch.cyberduck.core.LocaleFactory;
import ch.cyberduck.core.Path;
import ch.cyberduck.core.Session;
import ch.cyberduck.core.SimplePathPredicate;
import ch.cyberduck.core.exception.AccessDeniedException;
import ch.cyberduck.core.exception.BackgroundException;
import ch.cyberduck.core.exception.InvalidFilenameException;
Expand Down Expand Up @@ -85,15 +86,19 @@ default void preflight(final Path source, final Path target) throws BackgroundEx
throw new AccessDeniedException(MessageFormat.format(LocaleFactory.localizedString("Cannot rename {0}", "Error"),
source.getName())).withFile(source);
}
// Deny move to self
if(new SimplePathPredicate(source).test(target)) {
throw new AccessDeniedException(MessageFormat.format(LocaleFactory.localizedString("Cannot rename {0}", "Error"),
source.getName())).withFile(source);
}
}


/**
* @return Supported features
*/
default EnumSet<Flags> features(Path source, Path target) {
return EnumSet.noneOf(Flags.class);
}
default EnumSet<Flags> features(Path source, Path target) {
return EnumSet.noneOf(Flags.class);
}

/**
* Feature flags
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public EnumSet<Flags> features(final Path source, final Path target) {
@Override
public void preflight(final Path source, final Path target) throws BackgroundException {
if(!vault.getFilenameProvider().isValid(target.getName())) {
throw new InvalidFilenameException(MessageFormat.format(LocaleFactory.localizedString("Cannot create {0}", "Error"), target.getName()));
throw new InvalidFilenameException(MessageFormat.format(LocaleFactory.localizedString("Cannot create {0}", "Error"), target.getName())).withFile(source);
}
proxy.preflight(source, target);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public EnumSet<Flags> features(final Path source, final Path target) {
@Override
public void preflight(final Path source, final Path target) throws BackgroundException {
if(!vault.getFilenameProvider().isValid(target.getName())) {
throw new InvalidFilenameException(MessageFormat.format(LocaleFactory.localizedString("Cannot create {0}", "Error"), target.getName()));
throw new InvalidFilenameException(MessageFormat.format(LocaleFactory.localizedString("Cannot create {0}", "Error"), target.getName())).withFile(source);
}
proxy.preflight(source, target);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public CteraMoveFeature(final CteraSession session) {
@Override
public void preflight(final Path source, final Path target) throws BackgroundException {
if(!CteraTouchFeature.validate(target.getName())) {
throw new InvalidFilenameException(MessageFormat.format(LocaleFactory.localizedString("Cannot rename {0}", "Error"), source.getName())).withFile(source);
throw new InvalidFilenameException(MessageFormat.format(LocaleFactory.localizedString("Cannot rename {0}", "Error"), target.getName())).withFile(source);
}
assumeRole(source, DELETEPERMISSION);
// defaults to Acl.EMPTY (disabling role checking) if target does not exist
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ public void preflight(final Path source, final Path target) throws BackgroundExc
throw new AccessDeniedException(MessageFormat.format(LocaleFactory.localizedString("Cannot rename {0}", "Error"), source.getName())).withFile(source);
}
if(target.isRoot() || new DeepboxPathContainerService(session, fileid).isContainer(target) || new DeepboxPathContainerService(session, fileid).isInTrash(target)) {
throw new AccessDeniedException(MessageFormat.format(LocaleFactory.localizedString("Cannot create {0}", "Error"), target.getName()));
throw new AccessDeniedException(MessageFormat.format(LocaleFactory.localizedString("Cannot create {0}", "Error"), target.getName())).withFile(source);
}
final Acl acl = source.attributes().getAcl();
if(Acl.EMPTY == acl) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import ch.cyberduck.core.transfer.TransferStatus;
import ch.cyberduck.test.IntegrationTest;

import org.apache.commons.lang3.StringUtils;
import org.junit.Test;
import org.junit.experimental.categories.Category;

Expand Down Expand Up @@ -66,10 +67,11 @@ public void testFindDirectory() throws Exception {
@Test
public void testFindFile() throws Exception {
final Path box = new Path("/ORG 4 - DeepBox Desktop App/ORG 4 - DeepBox Desktop App/ORG3:Box1/Documents", EnumSet.of(Path.Type.directory, Path.Type.volume));
final Path file = new Path(box, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
final Path file = new Path(box, StringUtils.lowerCase(new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file));
final DeepboxIdProvider nodeid = new DeepboxIdProvider(session);
new DeepboxTouchFeature(session, nodeid).touch(file, new TransferStatus());
assertTrue(new DeepboxFindFeature(session, nodeid).find(file));
assertFalse(new DeepboxFindFeature(session, nodeid).find(new Path(box, StringUtils.upperCase(file.getName()), EnumSet.of(Path.Type.file))));
assertFalse(new DeepboxFindFeature(session, nodeid).find(new Path(file.getAbsolute(), EnumSet.of(Path.Type.directory))));
new DeepboxDeleteFeature(session, nodeid).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
import java.util.EnumSet;
import java.util.Objects;

import static ch.cyberduck.core.features.Copy.validate;

public class SDSCopyFeature implements Copy {
private static final Logger log = LogManager.getLogger(SDSCopyFeature.class);

Expand Down Expand Up @@ -101,5 +103,6 @@ public void preflight(final Path source, final Path target) throws BackgroundExc
log.warn("Deny copy of {} to {}", source, target);
throw new UnsupportedException(MessageFormat.format(LocaleFactory.localizedString("Cannot copy {0}", "Error"), source.getName())).withFile(source);
}
validate(session.getCaseSensitivity(), source, target);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ public void preflight(final Path source, final Path target) throws BackgroundExc
}
if(!SDSTouchFeature.validate(target.getName())) {
log.warn("Validation failed for target name {}", target);
throw new InvalidFilenameException(MessageFormat.format(LocaleFactory.localizedString("Cannot rename {0}", "Error"), source.getName())).withFile(target);
throw new InvalidFilenameException(MessageFormat.format(LocaleFactory.localizedString("Cannot rename {0}", "Error"), target.getName())).withFile(source);
}
final SDSPermissionsFeature acl = new SDSPermissionsFeature(session, nodeid);
if(!new SimplePathPredicate(source.getParent()).test(target.getParent())) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
import com.dropbox.core.v2.files.DbxUserFilesRequests;
import com.dropbox.core.v2.files.RelocationResult;

import static ch.cyberduck.core.features.Copy.validate;

public class DropboxCopyFeature implements Copy {
private static final Logger log = LogManager.getLogger(DropboxCopyFeature.class);

Expand Down Expand Up @@ -75,5 +77,6 @@ public void preflight(final Path source, final Path target) throws BackgroundExc
if(!DropboxTouchFeature.validate(target.getName())) {
throw new InvalidFilenameException(MessageFormat.format(LocaleFactory.localizedString("Cannot create {0}", "Error"), target.getName()));
}
validate(session.getCaseSensitivity(), source, target);
}
}
Loading

0 comments on commit 15a2cb9

Please sign in to comment.