content: /*
   * Licensed to the Apache Software Foundation (ASF) under one
   * or more contributor license agreements.  See the NOTICE file
   * distributed with this work for additional information
   * regarding copyright ownership.  The ASF licenses this file
   * to you 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
   *
   * http://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.apache.chemistry.opencmis.workbench.model;
  
  import java.net.Authenticator;
  import java.security.cert.X509Certificate;
  import java.util.Collections;
  import java.util.HashSet;
  import java.util.List;
  import java.util.Locale;
  import java.util.Map;
  import java.util.Properties;
  import java.util.Set;
  
  import javax.net.ssl.HostnameVerifier;
  import javax.net.ssl.HttpsURLConnection;
  import javax.net.ssl.SSLContext;
  import javax.net.ssl.SSLSession;
  import javax.net.ssl.TrustManager;
  import javax.net.ssl.X509TrustManager;
  
  import org.apache.chemistry.opencmis.client.SessionParameterMap;
  import org.apache.chemistry.opencmis.client.api.ObjectFactory;
  import org.apache.chemistry.opencmis.client.api.ObjectType;
  import org.apache.chemistry.opencmis.client.api.OperationContext;
  import org.apache.chemistry.opencmis.client.api.Repository;
  import org.apache.chemistry.opencmis.client.api.Session;
  import org.apache.chemistry.opencmis.client.bindings.cache.TypeDefinitionCache;
  import org.apache.chemistry.opencmis.client.bindings.impl.ClientVersion;
  import org.apache.chemistry.opencmis.client.runtime.SessionFactoryImpl;
  import org.apache.chemistry.opencmis.client.runtime.cache.Cache;
  import org.apache.chemistry.opencmis.commons.PropertyIds;
  import org.apache.chemistry.opencmis.commons.SessionParameter;
  import org.apache.chemistry.opencmis.commons.data.RepositoryInfo;
  import org.apache.chemistry.opencmis.commons.definitions.PropertyDefinition;
  import org.apache.chemistry.opencmis.commons.enums.BaseTypeId;
  import org.apache.chemistry.opencmis.commons.enums.BindingType;
  import org.apache.chemistry.opencmis.commons.enums.CapabilityAcl;
  import org.apache.chemistry.opencmis.commons.enums.IncludeRelationships;
  import org.apache.chemistry.opencmis.commons.spi.AuthenticationProvider;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  public class ClientSession {
  
      private static final Logger LOG = LoggerFactory.getLogger(ClientSession.class);
  
      public static final String WORKBENCH_USER_AGENT = "OpenCMIS-Workbench/"
              + (ClientVersion.OPENCMIS_VERSION == null ? "?" : ClientVersion.OPENCMIS_VERSION) + " "
              + ClientVersion.OPENCMIS_USER_AGENT;
  
      public static final String WORKBENCH_PREFIX = "cmis.workbench.";
      public static final String OBJECT_PREFIX = WORKBENCH_PREFIX + "object.";
      public static final String FOLDER_PREFIX = WORKBENCH_PREFIX + "folder.";
      public static final String VERSION_PREFIX = WORKBENCH_PREFIX + "version.";
      public static final String ACCEPT_SELF_SIGNED_CERTIFICATES = WORKBENCH_PREFIX + "acceptSelfSignedCertificates";
      public static final String MAX_FOLDER_CHILDREN = FOLDER_PREFIX + "maxChildren";
  
      public enum Authentication {
          NONE, STANDARD, NTLM, OAUTH_BEARER, CLIENT_CERT
      }
  
      private static final Set<String> FOLDER_PROPERTY_SET = new HashSet<String>();
      static {
          FOLDER_PROPERTY_SET.add(PropertyIds.OBJECT_ID);
          FOLDER_PROPERTY_SET.add(PropertyIds.OBJECT_TYPE_ID);
          FOLDER_PROPERTY_SET.add(PropertyIds.NAME);
          FOLDER_PROPERTY_SET.add(PropertyIds.CONTENT_STREAM_MIME_TYPE);
          FOLDER_PROPERTY_SET.add(PropertyIds.CONTENT_STREAM_LENGTH);
          FOLDER_PROPERTY_SET.add(PropertyIds.CONTENT_STREAM_FILE_NAME);
          FOLDER_PROPERTY_SET.add(PropertyIds.CREATED_BY);
          FOLDER_PROPERTY_SET.add(PropertyIds.CREATION_DATE);
          FOLDER_PROPERTY_SET.add(PropertyIds.LAST_MODIFIED_BY);
          FOLDER_PROPERTY_SET.add(PropertyIds.LAST_MODIFICATION_DATE);
          FOLDER_PROPERTY_SET.add(PropertyIds.IS_VERSION_SERIES_CHECKED_OUT);
          FOLDER_PROPERTY_SET.add(PropertyIds.VERSION_SERIES_CHECKED_OUT_ID);
      }
  
      private static final Set<String> VERSION_PROPERTY_SET = new HashSet<String>();
      static {
          VERSION_PROPERTY_SET.add(PropertyIds.OBJECT_ID);
          VERSION_PROPERTY_SET.add(PropertyIds.OBJECT_TYPE_ID);
          VERSION_PROPERTY_SET.add(PropertyIds.NAME);
          VERSION_PROPERTY_SET.add(PropertyIds.VERSION_LABEL);
          VERSION_PROPERTY_SET.add(PropertyIds.IS_LATEST_VERSION);
          VERSION_PROPERTY_SET.add(PropertyIds.IS_MAJOR_VERSION);
          VERSION_PROPERTY_SET.add(PropertyIds.IS_LATEST_MAJOR_VERSION);
          VERSION_PROPERTY_SET.add(PropertyIds.CONTENT_STREAM_MIME_TYPE);
          VERSION_PROPERTY_SET.add(PropertyIds.CONTENT_STREAM_LENGTH);
          VERSION_PROPERTY_SET.add(PropertyIds.CONTENT_STREAM_FILE_NAME);
      }
  
      private Map<String, String> sessionParameters;
      private List<Repository> repositories;
      private Session session;
      private OperationContext objectOperationContext;
      private OperationContext folderOperationContext;
      private OperationContext versionOperationContext;
      private int maxChildren;
      private String startFolderId;
  
      public ClientSession(Map<String, String> sessionParameters, ObjectFactory objectFactory,
              AuthenticationProvider authenticationProvider, Cache cache, TypeDefinitionCache typeDefCache) {
          if (sessionParameters == null) {
              throw new IllegalArgumentException("Parameters must not be null!");
          }
  
          connect(sessionParameters, objectFactory, authenticationProvider, cache, typeDefCache);
      }
  
      public static SessionParameterMap createSessionParameters(String url, BindingType binding, String username,
              String password, Authentication authentication, boolean compression, boolean clientCompression,
              boolean cookies, String csrfHeader, Locale locale, long connectionTimeout, long readTimeout) {
          SessionParameterMap parameters = new SessionParameterMap();
  
          switch (binding) {
          case WEBSERVICES:
              parameters.setWebServicesBindingUrl(url);
              break;
          case ATOMPUB:
              parameters.setAtomPubBindingUrl(url);
              break;
          case BROWSER:
              parameters.setBrowserBindingUrl(url);
              break;
          default:
              parameters.put(SessionParameter.BINDING_TYPE, BindingType.CUSTOM.value());
          }
  
          switch (authentication) {
          case STANDARD:
              parameters.setUsernameTokenAuthentication(username, password, true);
              break;
          case NTLM:
              parameters.setNtlmAuthentication(username, password);
              break;
          case OAUTH_BEARER:
              parameters.setOAuthBearerTokenAuthentication(username);
              break;
          case CLIENT_CERT:
              parameters.setCertificateAuthentication(username, password);
              break;
          default:
              parameters.setNoAuthentication();
          }
  
          parameters.setUserAgent(WORKBENCH_USER_AGENT);
  
          parameters.setCompression(compression);
          parameters.setClientCompression(clientCompression);
  
          parameters.setCookies(cookies);
  
          if (csrfHeader != null) {
              String ch = csrfHeader.trim();
              if (ch.length() > 0) {
                  parameters.setCsrfHeader(ch);
              }
          }
  
          if (locale != null) {
              parameters.setLocale(locale);
          }
  
          if (connectionTimeout > 0) {
              parameters.setConnectionTimeout(connectionTimeout);
          } else {
              parameters.setConnectionTimeout(0);
          }
  
          if (readTimeout > 0) {
              parameters.setReadTimeout(readTimeout);
          } else {
              parameters.setReadTimeout(0);
          }
  
          // get additional workbench properties from system properties
          Properties sysProps = System.getProperties();
          for (String key : sysProps.stringPropertyNames()) {
              if (key.startsWith(WORKBENCH_PREFIX)) {
                  parameters.put(key, sysProps.getProperty(key));
              }
          }
  
          // set proxy authentication
          if (url != null) {
              String urlLower = url.trim().toLowerCase(Locale.ENGLISH);
  
              if (urlLower.startsWith("http://")) {
                  String proxyUser = System.getProperty("http.proxyUser");
                  if (proxyUser != null) {
                      String proxyPassword = System.getProperty("http.proxyPassword");
                      parameters.setProxyUserAndPassword(proxyUser, proxyPassword);
                  }
              } else if (urlLower.startsWith("https://")) {
                  String proxyUser = System.getProperty("https.proxyUser");
                  if (proxyUser != null) {
                      String proxyPassword = System.getProperty("https.proxyPassword");
                      parameters.setProxyUserAndPassword(proxyUser, proxyPassword);
                  }
              }
          }
  
          return parameters;
      }
  
      private void connect(Map<String, String> sessionParameters, ObjectFactory objectFactory,
              AuthenticationProvider authenticationProvider, Cache cache, TypeDefinitionCache typeDefCache) {
          this.sessionParameters = sessionParameters;
  
          // set a new dummy authenticator
          // don't send previous credentials to another server
          Authenticator.setDefault(new Authenticator() {
          });
  
          if (Boolean.parseBoolean(sessionParameters.get(ACCEPT_SELF_SIGNED_CERTIFICATES))) {
              acceptSelfSignedCertificates();
          }
  
          maxChildren = -1;
          String maxChildrenStr = sessionParameters.get(MAX_FOLDER_CHILDREN);
          if (maxChildrenStr != null) {
              try {
                  maxChildren = Integer.valueOf(maxChildrenStr.trim());
              } catch (NumberFormatException e) {
                  LOG.warn("Invalid {} parameter!", MAX_FOLDER_CHILDREN, e);
              }
          }
  
          repositories = SessionFactoryImpl.newInstance().getRepositories(sessionParameters, objectFactory,
                  authenticationProvider, cache, typeDefCache);
      }
  
      public synchronized List<Repository> getRepositories() {
          return repositories;
      }
  
      public synchronized Session createSession(int index) {
          session = repositories.get(index).createSession();
          objectOperationContext = null;
          folderOperationContext = null;
          versionOperationContext = null;
          return getSession();
      }
  
      public synchronized Session getSession() {
          return session;
      }
  
      public Map<String, String> getSessionParameters() {
          return Collections.unmodifiableMap(sessionParameters);
      }
  
      public synchronized void setStartFolderId(String startFolderId) {
          this.startFolderId = startFolderId;
      }
  
      public synchronized String getStartFolderId() {
          if (startFolderId != null) {
              return startFolderId;
          } else {
              return getSession().getRepositoryInfo().getRootFolderId();
          }
      }
  
      public synchronized int getMaxChildren() {
          return maxChildren;
      }
  
      public synchronized OperationContext getObjectOperationContext() {
          if (objectOperationContext == null) {
              createOperationContexts();
          }
          return objectOperationContext;
      }
  
      public synchronized OperationContext getFolderOperationContext() {
          if (folderOperationContext == null) {
              createOperationContexts();
          }
          return folderOperationContext;
      }
  
      public synchronized OperationContext getVersionOperationContext() {
          if (versionOperationContext == null) {
              createOperationContexts();
          }
          return versionOperationContext;
      }
  
      private void createOperationContexts() {
  
          RepositoryInfo repositoryInfo = getSession().getRepositoryInfo();
  
          String supportsAcl = "true";
          if (repositoryInfo != null && repositoryInfo.getCapabilities() != null
                  && repositoryInfo.getCapabilities().getAclCapability() == CapabilityAcl.NONE) {
              supportsAcl = "false";
          }
  
          // object operation context
          setDefault(OBJECT_PREFIX, sessionParameters, ClientOperationContext.FILTER, "*");
          setDefault(OBJECT_PREFIX, sessionParameters, ClientOperationContext.INCLUDE_ACLS, supportsAcl);
          setDefault(OBJECT_PREFIX, sessionParameters, ClientOperationContext.INCLUDE_ALLOWABLE_ACTIONS, "true");
          setDefault(OBJECT_PREFIX, sessionParameters, ClientOperationContext.INCLUDE_POLICIES, "true");
          setDefault(OBJECT_PREFIX, sessionParameters, ClientOperationContext.INCLUDE_RELATIONSHIPS,
                  IncludeRelationships.BOTH.value());
          setDefault(OBJECT_PREFIX, sessionParameters, ClientOperationContext.RENDITION_FILTER, "*");
          setDefault(OBJECT_PREFIX, sessionParameters, ClientOperationContext.ORDER_BY, null);
          setDefault(OBJECT_PREFIX, sessionParameters, ClientOperationContext.MAX_ITEMS_PER_PAGE, "1000");
  
          objectOperationContext = new ClientOperationContext(OBJECT_PREFIX, sessionParameters);
  
          // folder operation context
          if (!sessionParameters.containsKey(FOLDER_PREFIX + ClientOperationContext.FILTER)) {
              ObjectType type = session.getTypeDefinition(BaseTypeId.CMIS_DOCUMENT.value());
  
              StringBuilder filter = new StringBuilder(128);
              for (String propId : FOLDER_PROPERTY_SET) {
                  PropertyDefinition<?> propDef = type.getPropertyDefinitions().get(propId);
                  if (propDef != null) {
                      if (filter.length() > 0) {
                          filter.append(',');
                      }
                      filter.append(propDef.getQueryName());
                  }
              }
  
              sessionParameters.put(FOLDER_PREFIX + ClientOperationContext.FILTER, filter.toString());
          }
  
          setDefault(FOLDER_PREFIX, sessionParameters, ClientOperationContext.INCLUDE_ACLS, "false");
          setDefault(FOLDER_PREFIX, sessionParameters, ClientOperationContext.INCLUDE_ALLOWABLE_ACTIONS, "false");
          setDefault(FOLDER_PREFIX, sessionParameters, ClientOperationContext.INCLUDE_POLICIES, "false");
          setDefault(FOLDER_PREFIX, sessionParameters, ClientOperationContext.INCLUDE_RELATIONSHIPS,
                  IncludeRelationships.NONE.value());
          setDefault(FOLDER_PREFIX, sessionParameters, ClientOperationContext.RENDITION_FILTER, "cmis:none");
          setDefault(FOLDER_PREFIX, sessionParameters, ClientOperationContext.ORDER_BY, null);
          setDefault(FOLDER_PREFIX, sessionParameters, ClientOperationContext.MAX_ITEMS_PER_PAGE, "10000");
  
          folderOperationContext = new ClientOperationContext(FOLDER_PREFIX, sessionParameters);
  
          if (!sessionParameters.containsKey(VERSION_PREFIX + ClientOperationContext.FILTER)) {
              ObjectType type = session.getTypeDefinition(BaseTypeId.CMIS_DOCUMENT.value());
  
              StringBuilder filter = new StringBuilder(128);
              for (String propId : VERSION_PROPERTY_SET) {
                  PropertyDefinition<?> propDef = type.getPropertyDefinitions().get(propId);
                  if (propDef != null) {
                      if (filter.length() > 0) {
                          filter.append(',');
                      }
                      filter.append(propDef.getQueryName());
                  }
              }
  
              sessionParameters.put(VERSION_PREFIX + ClientOperationContext.FILTER, filter.toString());
          }
  
          setDefault(VERSION_PREFIX, sessionParameters, ClientOperationContext.INCLUDE_ACLS, "false");
          setDefault(VERSION_PREFIX, sessionParameters, ClientOperationContext.INCLUDE_ALLOWABLE_ACTIONS, "false");
          setDefault(VERSION_PREFIX, sessionParameters, ClientOperationContext.INCLUDE_POLICIES, "false");
          setDefault(VERSION_PREFIX, sessionParameters, ClientOperationContext.INCLUDE_RELATIONSHIPS,
                  IncludeRelationships.NONE.value());
          setDefault(VERSION_PREFIX, sessionParameters, ClientOperationContext.RENDITION_FILTER, "cmis:none");
          setDefault(VERSION_PREFIX, sessionParameters, ClientOperationContext.MAX_ITEMS_PER_PAGE, "10000");
  
          versionOperationContext = new ClientOperationContext(VERSION_PREFIX, sessionParameters);
      }
  
      private void setDefault(String prefix, Map<String, String> map, String key, String value) {
          if (!map.containsKey(prefix + key)) {
              map.put(prefix + key, value);
          }
      }
  
      private void acceptSelfSignedCertificates() {
          TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
              @Override
              public X509Certificate[] getAcceptedIssuers() {
                  return new X509Certificate[0];
              }
  
              @Override
              public void checkClientTrusted(X509Certificate[] certs, String authType) {
              }
  
              @Override
              public void checkServerTrusted(X509Certificate[] certs, String authType) {
              }
          } };
  
          HostnameVerifier accepctAllHostnames = new HostnameVerifier() {
              @Override
              public boolean verify(String hostname, SSLSession session) {
                  return true;
              }
          };
  
          try {
              SSLContext sc = SSLContext.getInstance("TLS");
              sc.init(null, trustAllCerts, new java.security.SecureRandom());
              HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
              HttpsURLConnection.setDefaultHostnameVerifier(accepctAllHostnames);
          } catch (Exception e) {
              LOG.error(e.getMessage(), e);
          }
      }
  }
  