Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow setting password for share only for pro and business accounts #14595

Merged
merged 3 commits into from
May 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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