ch.cyberduck.core.sds.SDSSession.java Source code

Java tutorial

Introduction

Here is the source code for ch.cyberduck.core.sds.SDSSession.java

Source

package ch.cyberduck.core.sds;

/*
 * Copyright (c) 2002-2017 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 2 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.AttributedList;
import ch.cyberduck.core.Credentials;
import ch.cyberduck.core.ExpiringObjectHolder;
import ch.cyberduck.core.Host;
import ch.cyberduck.core.HostKeyCallback;
import ch.cyberduck.core.HostPasswordStore;
import ch.cyberduck.core.HostUrlProvider;
import ch.cyberduck.core.ListProgressListener;
import ch.cyberduck.core.LocaleFactory;
import ch.cyberduck.core.LoginCallback;
import ch.cyberduck.core.LoginOptions;
import ch.cyberduck.core.Path;
import ch.cyberduck.core.PreferencesUseragentProvider;
import ch.cyberduck.core.UrlProvider;
import ch.cyberduck.core.exception.BackgroundException;
import ch.cyberduck.core.exception.PartialLoginFailureException;
import ch.cyberduck.core.features.*;
import ch.cyberduck.core.http.HttpSession;
import ch.cyberduck.core.oauth.OAuth2ErrorResponseInterceptor;
import ch.cyberduck.core.oauth.OAuth2RequestInterceptor;
import ch.cyberduck.core.preferences.PreferencesFactory;
import ch.cyberduck.core.sds.io.swagger.client.ApiException;
import ch.cyberduck.core.sds.io.swagger.client.JSON;
import ch.cyberduck.core.sds.io.swagger.client.api.AuthApi;
import ch.cyberduck.core.sds.io.swagger.client.api.ConfigApi;
import ch.cyberduck.core.sds.io.swagger.client.api.UserApi;
import ch.cyberduck.core.sds.io.swagger.client.model.KeyValueEntry;
import ch.cyberduck.core.sds.io.swagger.client.model.LoginRequest;
import ch.cyberduck.core.sds.io.swagger.client.model.UserKeyPairContainer;
import ch.cyberduck.core.sds.provider.HttpComponentsProvider;
import ch.cyberduck.core.ssl.ThreadLocalHostnameDelegatingTrustManager;
import ch.cyberduck.core.ssl.X509KeyManager;
import ch.cyberduck.core.ssl.X509TrustManager;
import ch.cyberduck.core.threading.CancelCallback;

import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpException;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpRequest;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.protocol.HttpContext;
import org.apache.log4j.Logger;
import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.jackson.JacksonFeature;
import org.glassfish.jersey.media.multipart.MultiPartFeature;
import org.glassfish.jersey.message.internal.InputStreamProvider;

import javax.ws.rs.client.ClientBuilder;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import com.migcomponents.migbase64.Base64;

public class SDSSession extends HttpSession<SDSApiClient> {
    private static final Logger log = Logger.getLogger(SDSSession.class);

    public static final String SDS_AUTH_TOKEN_HEADER = "X-Sds-Auth-Token";
    public static final int DEFAULT_CHUNKSIZE = 16;

    protected final SDSErrorResponseInterceptor retryHandler = new SDSErrorResponseInterceptor(this);

    private final OAuth2RequestInterceptor authorizationService;

    private final ExpiringObjectHolder<UserAccountWrapper> userAccount = new ExpiringObjectHolder<>(
            PreferencesFactory.get().getLong("sds.encryption.keys.ttl"));

    private final ExpiringObjectHolder<UserKeyPairContainer> keyPair = new ExpiringObjectHolder<>(
            PreferencesFactory.get().getLong("sds.encryption.keys.ttl"));

    private final List<KeyValueEntry> configuration = new ArrayList<>();

    public SDSSession(final Host host, final X509TrustManager trust, final X509KeyManager key) {
        super(host, new ThreadLocalHostnameDelegatingTrustManager(trust, host.getHostname()), key);
        this.authorizationService = new OAuth2RequestInterceptor(
                builder.build(this).addInterceptorLast(new HttpRequestInterceptor() {
                    @Override
                    public void process(final HttpRequest request, final HttpContext context)
                            throws HttpException, IOException {
                        request.addHeader(HttpHeaders.AUTHORIZATION,
                                String.format("Basic %s", Base64.encodeToString(
                                        String.format("%s:%s", host.getProtocol().getOAuthClientId(),
                                                host.getProtocol().getOAuthClientSecret()).getBytes("UTF-8"),
                                        false)));
                    }
                }).build(), host).withRedirectUri(host.getProtocol().getOAuthRedirectUrl());
    }

    @Override
    protected SDSApiClient connect(final HostKeyCallback key) throws BackgroundException {
        final HttpClientBuilder configuration = builder.build(this);
        switch (SDSProtocol.Authorization.valueOf(host.getProtocol().getAuthorization())) {
        case oauth:
            configuration
                    .setServiceUnavailableRetryStrategy(new OAuth2ErrorResponseInterceptor(authorizationService));
            configuration.addInterceptorLast(authorizationService);
            configuration.addInterceptorLast(new HttpRequestInterceptor() {
                @Override
                public void process(final HttpRequest request, final HttpContext context)
                        throws HttpException, IOException {
                    request.removeHeaders(SDSSession.SDS_AUTH_TOKEN_HEADER);
                }
            });
            break;
        default:
            configuration.setServiceUnavailableRetryStrategy(retryHandler);
            configuration.addInterceptorLast(retryHandler);
            break;
        }
        final CloseableHttpClient apache = configuration.build();
        final SDSApiClient client = new SDSApiClient(apache);
        client.setBasePath(new HostUrlProvider(false, true).get(host.getProtocol().getScheme(), host.getPort(),
                null, host.getHostname(), host.getProtocol().getContext()));
        client.setHttpClient(ClientBuilder.newClient(new ClientConfig().register(new InputStreamProvider())
                .register(MultiPartFeature.class).register(new JSON()).register(JacksonFeature.class)
                .connectorProvider(new HttpComponentsProvider(apache))));
        client.setUserAgent(new PreferencesUseragentProvider().get());
        return client;
    }

    @Override
    public void login(final HostPasswordStore keychain, final LoginCallback controller, final CancelCallback cancel)
            throws BackgroundException {
        final String login = host.getCredentials().getUsername();
        final String password = host.getCredentials().getPassword();
        // The provided token is valid for two hours, every usage resets this period to two full hours again. Logging off invalidates the token.
        switch (SDSProtocol.Authorization.valueOf(host.getProtocol().getAuthorization())) {
        case oauth:
            authorizationService.setTokens(authorizationService.authorize(host, keychain, controller, cancel));
            break;
        case radius:
            final Credentials additional = controller.prompt(host, host.getCredentials().getUsername(),
                    LocaleFactory.localizedString("Provide additional login credentials", "Credentials"),
                    LocaleFactory.localizedString("Multi-Factor Authentication", "S3"),
                    new LoginOptions(host.getProtocol()).user(false).keychain(false));
            // Save tokens for 401 error response when expired
            retryHandler.setTokens(login, password,
                    this.login(controller, new LoginRequest().authType(host.getProtocol().getAuthorization())
                            .login(login).password(additional.getPassword())));
            break;
        default:
            // Save tokens for 401 error response when expired
            retryHandler.setTokens(login, password, this.login(controller, new LoginRequest()
                    .authType(host.getProtocol().getAuthorization()).login(login).password(password)));
            break;
        }
        try {
            configuration.addAll(new ConfigApi(client).getSystemSettings(StringUtils.EMPTY).getItems());
        } catch (ApiException e) {
            // Precondition: Right "Config Read" required.
            log.warn(String.format("Ignore failure reading configuration. %s",
                    new SDSExceptionMappingService().map(e).getDetail()));
        }
    }

    private String login(final LoginCallback controller, final LoginRequest request) throws BackgroundException {
        try {
            try {
                return new AuthApi(client).login(request).getToken();
            } catch (ApiException e) {
                throw new SDSExceptionMappingService().map(e);
            }
        } catch (PartialLoginFailureException e) {
            final String username = host.getCredentials().getUsername();
            final Credentials additional = controller.prompt(host, username,
                    LocaleFactory.localizedString("Provide additional login credentials", "Credentials"),
                    e.getDetail(),
                    new LoginOptions(host.getProtocol()).user(false).keychain(false).usernamePlaceholder(username));
            return this.login(controller, new LoginRequest().authType(host.getProtocol().getAuthorization())
                    .password(additional.getPassword()));
        }
    }

    public UserAccountWrapper userAccount() throws BackgroundException {
        if (this.userAccount.get() == null) {
            try {
                userAccount.set(new UserAccountWrapper(
                        new UserApi(this.getClient()).getUserInfo(StringUtils.EMPTY, null, false)));
            } catch (ApiException e) {
                throw new SDSExceptionMappingService().map(e);
            }
        }
        return this.userAccount.get();
    }

    public UserKeyPairContainer keyPair() throws BackgroundException {
        if (this.keyPair.get() == null) {
            try {
                keyPair.set(new UserApi(this.getClient()).getUserKeyPair(StringUtils.EMPTY));
            } catch (ApiException e) {
                throw new SDSExceptionMappingService().map(e);
            }
        }
        return this.keyPair.get();
    }

    public List<KeyValueEntry> configuration() {
        return configuration;
    }

    @Override
    protected void logout() throws BackgroundException {
        client.getHttpClient().close();
    }

    @Override
    public AttributedList<Path> list(final Path directory, final ListProgressListener listener)
            throws BackgroundException {
        return new SDSListService(this).list(directory, listener);
    }

    @Override
    @SuppressWarnings("unchecked")
    public <T> T _getFeature(final Class<T> type) {
        if (type == Read.class) {
            return (T) new SDSDelegatingReadFeature(this, new SDSReadFeature(this));
        }
        if (type == Write.class) {
            return (T) new SDSDelegatingWriteFeature(this, new SDSWriteFeature(this));
        }
        if (type == MultipartWrite.class) {
            return (T) new SDSDelegatingWriteFeature(this, new SDSMultipartWriteFeature(this));
        }
        if (type == Directory.class) {
            return (T) new SDSDirectoryFeature(this);
        }
        if (type == Delete.class) {
            return (T) new SDSDeleteFeature(this);
        }
        if (type == IdProvider.class) {
            return (T) new SDSNodeIdProvider(this);
        }
        if (type == Touch.class) {
            return (T) new SDSTouchFeature(this);
        }
        if (type == Find.class) {
            return (T) new SDSFindFeature(this);
        }
        if (type == AttributesFinder.class) {
            return (T) new SDSAttributesFinderFeature(this);
        }
        if (type == Move.class) {
            return (T) new SDSDelegatingMoveFeature(this, new SDSMoveFeature(this));
        }
        if (type == Copy.class) {
            return (T) new SDSDelegatingCopyFeature(this, new SDSCopyFeature(this));
        }
        if (type == Bulk.class) {
            return (T) new SDSEncryptionBulkFeature(this);
        }
        if (type == Scheduler.class) {
            return (T) new SDSMissingFileKeysSchedulerFeature(this);
        }
        if (type == UrlProvider.class) {
            return (T) new SDSUrlProvider(this);
        }
        if (type == PromptUrlProvider.class) {
            return (T) new SDSSharesUrlProvider(this);
        }
        if (type == Quota.class) {
            return (T) new SDSQuotaFeature(this);
        }
        if (type == Search.class) {
            return (T) new SDSSearchFeature(this);
        }
        return super._getFeature(type);
    }
}