Skip to content

Commit

Permalink
Merge pull request #14595 from iterate-ch/bugfix/GH-14581
Browse files Browse the repository at this point in the history
Allow setting password for share only for pro and business accounts
  • Loading branch information
dkocher authored May 2, 2023
2 parents c67459c + b6c6626 commit 8acb874
Show file tree
Hide file tree
Showing 4 changed files with 171 additions and 99 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,120 +16,38 @@
*/

import ch.cyberduck.core.Credentials;
import ch.cyberduck.core.DescriptiveUrl;
import ch.cyberduck.core.Host;
import ch.cyberduck.core.LocaleFactory;
import ch.cyberduck.core.LoginOptions;
import ch.cyberduck.core.PasswordCallback;
import ch.cyberduck.core.Path;
import ch.cyberduck.core.PathContainerService;
import ch.cyberduck.core.exception.BackgroundException;
import ch.cyberduck.core.exception.ConflictException;
import ch.cyberduck.core.features.PromptUrlProvider;
import ch.cyberduck.core.exception.LoginCanceledException;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.net.URI;
import java.text.MessageFormat;
import java.util.List;

import com.dropbox.core.DbxException;
import com.dropbox.core.v2.filerequests.DbxUserFileRequestsRequests;
import com.dropbox.core.v2.filerequests.FileRequest;
import com.dropbox.core.v2.sharing.DbxUserSharingRequests;
import com.dropbox.core.v2.sharing.RequestedVisibility;
import com.dropbox.core.v2.sharing.SharedLinkMetadata;
import com.dropbox.core.v2.sharing.SharedLinkSettings;

public class DropboxPasswordShareUrlProvider implements PromptUrlProvider<Void, Void> {
private static final Logger log = LogManager.getLogger(DropboxPasswordShareUrlProvider.class);
public class DropboxPasswordShareUrlProvider extends DropboxShareUrlProvider {

private final DropboxSession session;
private final PathContainerService containerService;

public DropboxPasswordShareUrlProvider(final DropboxSession session) {
super(session);
this.session = session;
this.containerService = new DropboxPathContainerService(session);
}

@Override
public DescriptiveUrl toDownloadUrl(final Path file, final Void options, final PasswordCallback callback) throws BackgroundException {
try {
try {
final Host bookmark = session.getHost();
final SharedLinkSettings.Builder settings = SharedLinkSettings.newBuilder();
final Credentials password = callback.prompt(bookmark,
LocaleFactory.localizedString("Passphrase", "Cryptomator"),
MessageFormat.format(LocaleFactory.localizedString("Create a passphrase required to access {0}", "Credentials"), file.getName()),
new LoginOptions().anonymous(true).keychain(false).icon(bookmark.getProtocol().disk()));
if(password.isPasswordAuthentication()) {
settings.withLinkPassword(password.getPassword());
settings.withRequestedVisibility(RequestedVisibility.PASSWORD);
}
else {
settings.withRequestedVisibility(RequestedVisibility.PUBLIC);
}
final SharedLinkMetadata share = new DbxUserSharingRequests(session.getClient(file))
.createSharedLinkWithSettings(containerService.getKey(file),
settings.build());
if(log.isDebugEnabled()) {
log.debug(String.format("Created shared link %s", share));
}
return new DescriptiveUrl(URI.create(share.getUrl()), DescriptiveUrl.Type.signed,
MessageFormat.format(LocaleFactory.localizedString("{0} URL"),
LocaleFactory.localizedString("Password Share", "Dropbox"))
);
}
catch(DbxException e) {
throw new DropboxExceptionMappingService().map(e);
}
}
catch(ConflictException e) {
// Shared link already exists
try {
final List<SharedLinkMetadata> links = new DbxUserSharingRequests(session.getClient(file))
.listSharedLinksBuilder().withDirectOnly(true).withPath(containerService.getKey(file)).start().getLinks();
for(SharedLinkMetadata share : links) {
if(log.isDebugEnabled()) {
log.debug(String.format("Return existing shared link %s", share));
}
return new DescriptiveUrl(URI.create(share.getUrl()), DescriptiveUrl.Type.signed,
MessageFormat.format(LocaleFactory.localizedString("{0} URL"),
LocaleFactory.localizedString("Password Share", "Dropbox"))
);
}
throw e;
}
catch(DbxException f) {
throw e;
}
}
}

@Override
public DescriptiveUrl toUploadUrl(final Path file, final Void options, final PasswordCallback callback) throws BackgroundException {
try {
final FileRequest request = new DbxUserFileRequestsRequests(session.getClient())
.create(file.getName(), file.isRoot() ? file.getAbsolute() : containerService.getKey(file));
return new DescriptiveUrl(URI.create(request.getUrl()), DescriptiveUrl.Type.signed);
}
catch(DbxException e) {
throw new DropboxExceptionMappingService().map(e);
}
}

@Override
public boolean isSupported(final Path file, final Type type) {
switch(type) {
case download:
return true;
case upload:
if(file.isRoot()) {
return false;
}
return file.isDirectory();
protected SharedLinkSettings.Builder toSettings(final Path file, final PasswordCallback callback) throws LoginCanceledException {
final SharedLinkSettings.Builder settings = super.toSettings(file, callback);
final Host bookmark = session.getHost();
final Credentials password = callback.prompt(bookmark,
LocaleFactory.localizedString("Passphrase", "Cryptomator"),
MessageFormat.format(LocaleFactory.localizedString("Create a passphrase required to access {0}", "Credentials"), file.getName()),
new LoginOptions().anonymous(true).keychain(false).icon(bookmark.getProtocol().disk()));
if(password.isPasswordAuthentication()) {
settings.withLinkPassword(password.getPassword());
settings.withRequestedVisibility(RequestedVisibility.PASSWORD);
}
return false;
return settings;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,10 @@ public class DropboxSession extends HttpSession<CustomDbxRawClientV2> {
= new PreferencesUseragentProvider();

private OAuth2RequestInterceptor authorizationService;
private Lock<String> locking = null;

private DropboxLockFeature locking;
private DropboxShareUrlProvider share;

private PathRoot root = PathRoot.HOME;

public DropboxSession(final Host host, final X509TrustManager trust, final X509KeyManager key) {
Expand Down Expand Up @@ -98,8 +101,16 @@ public void login(final Proxy proxy, final LoginCallback prompt, final CancelCal
final Credentials credentials = host.getCredentials();
credentials.setUsername(account.getEmail());
switch(account.getAccountType()) {
// The features listed below are only available to customers on Dropbox Professional, Standard, Advanced, and Enterprise.
case PRO:
case BUSINESS:
share = new DropboxPasswordShareUrlProvider(this);
locking = new DropboxLockFeature(this);
break;
default:
share = new DropboxShareUrlProvider(this);
locking = null;
break;
}
// The Dropbox API Path Root is the folder that an API request operates relative to.
final PathRoot root = PathRoot.root(account.getRootInfo().getRootNamespaceId());
Expand Down Expand Up @@ -154,7 +165,7 @@ public <T> T _getFeature(Class<T> type) {
return (T) new DropboxUrlProvider(this);
}
if(type == PromptUrlProvider.class) {
return (T) new DropboxPasswordShareUrlProvider(this);
return (T) share;
}
if(type == Find.class) {
return (T) new DropboxFindFeature(this);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package ch.cyberduck.core.dropbox;

/*
* Copyright (c) 2002-2019 iterate GmbH. All rights reserved.
* https://cyberduck.io/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/

import ch.cyberduck.core.DescriptiveUrl;
import ch.cyberduck.core.LocaleFactory;
import ch.cyberduck.core.PasswordCallback;
import ch.cyberduck.core.Path;
import ch.cyberduck.core.PathContainerService;
import ch.cyberduck.core.exception.BackgroundException;
import ch.cyberduck.core.exception.ConflictException;
import ch.cyberduck.core.exception.LoginCanceledException;
import ch.cyberduck.core.features.PromptUrlProvider;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.net.URI;
import java.text.MessageFormat;
import java.util.List;

import com.dropbox.core.DbxException;
import com.dropbox.core.v2.filerequests.DbxUserFileRequestsRequests;
import com.dropbox.core.v2.filerequests.FileRequest;
import com.dropbox.core.v2.sharing.DbxUserSharingRequests;
import com.dropbox.core.v2.sharing.RequestedVisibility;
import com.dropbox.core.v2.sharing.SharedLinkMetadata;
import com.dropbox.core.v2.sharing.SharedLinkSettings;

public class DropboxShareUrlProvider implements PromptUrlProvider<Void, Void> {
private static final Logger log = LogManager.getLogger(DropboxShareUrlProvider.class);

private final DropboxSession session;
private final PathContainerService containerService;

public DropboxShareUrlProvider(final DropboxSession session) {
this.session = session;
this.containerService = new DropboxPathContainerService(session);
}

protected SharedLinkSettings.Builder toSettings(final Path file, final PasswordCallback callback) throws LoginCanceledException {
final SharedLinkSettings.Builder settings = SharedLinkSettings.newBuilder();
settings.withRequestedVisibility(RequestedVisibility.PUBLIC);
return settings;
}

@Override
public DescriptiveUrl toDownloadUrl(final Path file, final Void options, final PasswordCallback callback) throws BackgroundException {
final SharedLinkSettings settings = this.toSettings(file, callback).build();
try {
try {
final SharedLinkMetadata share = new DbxUserSharingRequests(session.getClient(file))
.createSharedLinkWithSettings(containerService.getKey(file),
settings);
if(log.isDebugEnabled()) {
log.debug(String.format("Created shared link %s", share));
}
return new DescriptiveUrl(URI.create(share.getUrl()), DescriptiveUrl.Type.signed,
MessageFormat.format(LocaleFactory.localizedString("{0} URL"),
LocaleFactory.localizedString("Password Share", "Dropbox"))
);
}
catch(DbxException e) {
throw new DropboxExceptionMappingService().map(e);
}
}
catch(ConflictException e) {
// Shared link already exists
try {
final List<SharedLinkMetadata> links = new DbxUserSharingRequests(session.getClient(file))
.listSharedLinksBuilder().withDirectOnly(true).withPath(containerService.getKey(file)).start().getLinks();
for(SharedLinkMetadata link : links) {
if(log.isDebugEnabled()) {
log.debug(String.format("Return existing shared link %s", link));
}
final SharedLinkMetadata share = new DbxUserSharingRequests(session.getClient(file)).modifySharedLinkSettings(link.getUrl(),
settings);
return new DescriptiveUrl(URI.create(share.getUrl()), DescriptiveUrl.Type.signed,
MessageFormat.format(LocaleFactory.localizedString("{0} URL"),
LocaleFactory.localizedString("Password Share", "Dropbox"))
);
}
throw e;
}
catch(DbxException f) {
throw e;
}
}
}

@Override
public DescriptiveUrl toUploadUrl(final Path file, final Void options, final PasswordCallback callback) throws BackgroundException {
try {
final FileRequest request = new DbxUserFileRequestsRequests(session.getClient())
.create(file.getName(), file.isRoot() ? file.getAbsolute() : containerService.getKey(file));
return new DescriptiveUrl(URI.create(request.getUrl()), DescriptiveUrl.Type.signed);
}
catch(DbxException e) {
throw new DropboxExceptionMappingService().map(e);
}
}

@Override
public boolean isSupported(final Path file, final Type type) {
switch(type) {
case download:
return true;
case upload:
if(file.isRoot()) {
return false;
}
return file.isDirectory();
}
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@
import ch.cyberduck.core.AlphanumericRandomStringService;
import ch.cyberduck.core.Credentials;
import ch.cyberduck.core.DescriptiveUrl;
import ch.cyberduck.core.DisabledLoginCallback;
import ch.cyberduck.core.DisabledPasswordCallback;
import ch.cyberduck.core.Host;
import ch.cyberduck.core.LoginOptions;
import ch.cyberduck.core.Path;
import ch.cyberduck.core.exception.InteroperabilityException;
import ch.cyberduck.core.exception.LoginCanceledException;
import ch.cyberduck.core.features.Delete;
import ch.cyberduck.core.features.PromptUrlProvider;
Expand Down Expand Up @@ -86,6 +86,20 @@ public Credentials prompt(final Host bookmark, final String title, final String
new DropboxDeleteFeature(session).delete(Collections.singletonList(file), new DisabledPasswordCallback(), new Delete.DisabledCallback());
}

@Test
public void testShareFileDownloadPassword() throws Exception {
final Path file = new DropboxTouchFeature(session).touch(
new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus());
final DropboxPasswordShareUrlProvider provider = new DropboxPasswordShareUrlProvider(session);
assertThrows(InteroperabilityException.class, () -> provider.toDownloadUrl(file, null, new DisabledPasswordCallback() {
@Override
public Credentials prompt(final Host bookmark, final String title, final String reason, final LoginOptions options) throws LoginCanceledException {
return new Credentials().withPassword(new AlphanumericRandomStringService().random());
}
}));
new DropboxDeleteFeature(session).delete(Collections.singletonList(file), new DisabledPasswordCallback(), new Delete.DisabledCallback());
}

@Test
public void testShareDownloadFolderPublic() throws Exception {
final Path folder = new DropboxDirectoryFeature(session).mkdir(
Expand Down

0 comments on commit 8acb874

Please sign in to comment.