/* * 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. */ using PortCMIS.Binding.Http; using PortCMIS.Binding.Impl; using PortCMIS.Binding.Services; using PortCMIS.Client; using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; using System.Net.Http.Headers; using System.Text; namespace PortCMIS.Binding { /// /// Low-level CMIS binding interface. /// public interface ICmisBinding : IDisposable { /// /// Binding type. /// string BindingType { get; } /// /// Returns the repository service instance. /// /// the repository service instance IRepositoryService GetRepositoryService(); /// /// Returns the navigation service instance. /// /// the navigation service instance INavigationService GetNavigationService(); /// /// Returns the object service instance. /// /// the object service instance IObjectService GetObjectService(); /// /// Returns the versioning service instance. /// /// the versioning service instance IVersioningService GetVersioningService(); /// /// Returns the relationship service instance. /// /// the relationship service instance IRelationshipService GetRelationshipService(); /// /// Returns the discovery service instance. /// /// the discovery service instance IDiscoveryService GetDiscoveryService(); /// /// Returns the multi-filing service instance. /// /// the multi-filing service instance IMultiFilingService GetMultiFilingService(); /// /// Returns the ACL service instance. /// /// the ACL service instance IAclService GetAclService(); /// /// Returns the policy service instance. /// /// the policy service instance IPolicyService GetPolicyService(); /// /// Returns the authentication provider instance. /// /// the authentication provider instance IAuthenticationProvider GetAuthenticationProvider(); /// /// Clears all low-level caches. /// void ClearAllCaches(); /// /// Clears all low-level caches for the given repository. /// /// the repository ID void ClearRepositoryCache(string repositoryId); } /// /// Binding Session interface. /// public interface IBindingSession { /// /// Gets a value from the session. /// /// the key /// the value or null if the key is unknown object GetValue(string key); /// /// Gets a value from the session. /// /// the key /// the default value /// the value or the default value if the key is unknown object GetValue(string key, object defValue); /// /// Gets a value as an integer from the session. /// /// the key /// the default value /// the value or the default value if the key is unknown or the value cannot be returned as an integer int GetValue(string key, int defValue); /// /// Gets a value as a boolean from the session. /// /// the key /// the default value /// the value or the default value if the key is unknown or the value cannot be returned as a boolean bool GetValue(string key, bool defValue); /// /// Adds a key-value pair to the session. /// /// the key /// the value void PutValue(string key, object value); /// /// Removes a key-value pair. /// /// the key void RemoveValue(string key); /// /// Gets the authentication provider from the session. /// /// the authentication provider or null if no authentication provider has been set IAuthenticationProvider GetAuthenticationProvider(); /// /// Gets the HTTP invoker from the session. /// /// /// If no HTTP invoker has been set, a HTTP invoker is created, added to the session and returned. /// /// the HTTP invoker IHttpInvoker GetHttpInvoker(); } /// /// SPI interface. /// public interface ICmisSpi : IDisposable { /// /// Initializes the SPI with a binding session. /// /// the binding session void Initialize(IBindingSession session); /// /// Returns the repository service instance. /// IRepositoryService GetRepositoryService(); /// /// Returns the navigation service instance. /// INavigationService GetNavigationService(); /// /// Returns the object service instance. /// IObjectService GetObjectService(); /// /// Returns the versioning service instance. /// IVersioningService GetVersioningService(); /// /// Returns the relationship service instance. /// IRelationshipService GetRelationshipService(); /// /// Returns the discovery service instance. /// IDiscoveryService GetDiscoveryService(); /// /// Returns the multi-filing service instance. /// IMultiFilingService GetMultiFilingService(); /// /// Returns the ACL service instance. /// IAclService GetAclService(); /// /// Returns the repository service instance. /// IPolicyService GetPolicyService(); /// /// Clears all caches. /// void ClearAllCaches(); /// /// Clears all caches of a repository. /// /// the repository ID void ClearRepositoryCache(string repositoryId); } /// /// Authentication provider interface. /// public interface IAuthenticationProvider { /// /// Gets the binding session instance. /// IBindingSession Session { get; set; } } /// /// Authentication provider interface for the portable HTTP client. /// public interface IPortableAuthenticationProvider : IAuthenticationProvider { /// /// Creates a HttpClientHandler object. /// /// /// If this method returns null, a default HttpClientHandler object will be created. /// /// a new HttpClientHandler object HttpClientHandler CreateHttpClientHandler(); /// /// Prepares the HTTP client handler before it is used. /// /// the HTTP client handler void PrepareHttpClientHandler(HttpClientHandler httpClientHandler); /// /// Prepares the HTTP request message before it is used. /// /// the HTTP request message void PrepareHttpRequestMessage(HttpRequestMessage httpRequestMessage); /// /// Handles the HTTP response if necessary. /// /// the HTTP response message void HandleResponse(HttpResponseMessage httpResponseMessage); } /// /// Base implementation of a portable authentication provider. /// public abstract class AbstractAuthenticationProvider : IPortableAuthenticationProvider { /// public IBindingSession Session { get; set; } /// /// Gets the HTTP cookie container. /// public CookieContainer CookieContainer { get; private set; } /// /// Gets the user name. /// public string User { get { return Session.GetValue(SessionParameter.User) as string; } } /// /// Gets the Password. /// public string Password { get { return Session.GetValue(SessionParameter.Password) as string; } } /// /// Gets the proxy user. /// public string ProxyUser { get { return Session.GetValue(SessionParameter.ProxyUser) as string; } } /// /// Gets the proxy password /// public string ProxyPassword { get { return Session.GetValue(SessionParameter.ProxyPassword) as string; } } /// public virtual HttpClientHandler CreateHttpClientHandler() { return null; } /// public virtual void PrepareHttpClientHandler(HttpClientHandler httpClientHandler) { httpClientHandler.PreAuthenticate = true; httpClientHandler.UseCookies = true; httpClientHandler.UseProxy = true; CookieContainer = new CookieContainer(); httpClientHandler.CookieContainer = CookieContainer; } /// public virtual void PrepareHttpRequestMessage(HttpRequestMessage httpRequestMessage) { } /// public virtual void HandleResponse(HttpResponseMessage httpResponseMessage) { } } /// /// Standard Authentication Provider. /// public class StandardAuthenticationProvider : AbstractAuthenticationProvider { /// /// Gets the OAuth bearer token. /// public string BearerToken { get { return Session.GetValue(SessionParameter.OAuthBearerToken) as string; } } /// /// Gets the CSRF header. /// r public string CsrfHeader { get { return Session.GetValue(SessionParameter.CsrfHeader) as string; } } /// /// Gets the authentication header. /// protected AuthenticationHeaderValue AuthenticationHeader { get; set; } /// /// Gets the proxy authentication header. /// protected AuthenticationHeaderValue ProxyAuthenticationHeader { get; set; } private object tokenLock = new object(); private string token = "fetch"; /// /// Gets the CSRF header name. /// protected string CsrfHeaderName { get; set; } /// /// Gets the CSRF header token. /// protected string CsrfToken { get { lock (tokenLock) { return token; } } set { lock (tokenLock) { token = value; } } } /// public override void PrepareHttpClientHandler(HttpClientHandler httpClientHandler) { base.PrepareHttpClientHandler(httpClientHandler); if (User != null) { httpClientHandler.PreAuthenticate = true; string preemptiveFlag = Session.GetValue(SessionParameter.PreemptivAuthentication) as string; if (preemptiveFlag != null && preemptiveFlag.ToLowerInvariant().Equals("true")) { var userPassword = Encoding.UTF8.GetBytes(User + ":" + Password); AuthenticationHeader = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(userPassword)); } else { httpClientHandler.Credentials = new NetworkCredential(User, Password); } } else { if (BearerToken != null) { httpClientHandler.PreAuthenticate = true; httpClientHandler.UseDefaultCredentials = false; AuthenticationHeader = new AuthenticationHeaderValue("Bearer", BearerToken); } else { httpClientHandler.UseDefaultCredentials = true; } } if (ProxyUser != null) { var userPassword = Encoding.UTF8.GetBytes(ProxyUser + ":" + ProxyPassword); ProxyAuthenticationHeader = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(userPassword)); } if (CsrfHeader != null) { CsrfHeaderName = CsrfHeader; } } /// public override void PrepareHttpRequestMessage(HttpRequestMessage httpRequestMessage) { base.PrepareHttpRequestMessage(httpRequestMessage); if (AuthenticationHeader != null) { httpRequestMessage.Headers.Authorization = AuthenticationHeader; } if (ProxyAuthenticationHeader != null) { httpRequestMessage.Headers.ProxyAuthorization = ProxyAuthenticationHeader; } if (CsrfHeaderName != null && CsrfToken != null) { httpRequestMessage.Headers.Add(CsrfHeaderName, CsrfToken); } } /// public override void HandleResponse(HttpResponseMessage httpResponseMessage) { base.HandleResponse(httpResponseMessage); if (CsrfHeaderName != null) { IEnumerable values; if (httpResponseMessage.Headers.TryGetValues(CsrfHeaderName, out values)) { CsrfToken = values.First(); } } } } /// /// A factory that created low-level binding objects. /// public class CmisBindingFactory { /// /// Default CMIS AtomPub binding SPI implementation class name. /// public const string BindingSpiAtomPub = "PortCMIS.Binding.AtomPub.CmisAtomPubSpi"; /// /// Default CMIS Browser binding SPI implementation class name. /// public const string BindingSpiBrowser = "PortCMIS.Binding.Browser.CmisBrowserSpi"; /// /// Standard authentication provider class name. /// public const string StandardAuthenticationProviderClass = "PortCMIS.Binding.StandardAuthenticationProvider"; /// /// Default HTTP invoker class name. /// public const string DefaultHttpInvokerClass = "PortCMIS.Binding.Http.DefaultHttpInvoker"; private IDictionary defaults; /// /// This is a factory. /// private CmisBindingFactory() { defaults = CreateNewDefaultParameters(); } /// /// Creates a new instance of this factory. /// /// a factory object public static CmisBindingFactory NewInstance() { return new CmisBindingFactory(); } /// /// Gets and sets default session parameters. /// public IDictionary DefaultSessionParameters { get { return defaults; } set { if (value == null) { defaults = CreateNewDefaultParameters(); } else { defaults = value; } } } /// /// Creates a binding object for custom binding implementations. /// /// the session parameters /// an authentication provider instance or null to use the default implementation /// a low-level binding object public ICmisBinding CreateCmisBinding(IDictionary sessionParameters, IAuthenticationProvider authenticationProvider) { CheckSessionParameters(sessionParameters, true); AddDefaultParameters(sessionParameters); return new CmisBinding(sessionParameters, authenticationProvider); } /// /// Creates an Browser binding object. /// /// the session parameters /// an authentication provider instance or null to use the default implementation /// a low-level binding object public ICmisBinding CreateCmisBrowserBinding(IDictionary sessionParameters, IAuthenticationProvider authenticationProvider) { CheckSessionParameters(sessionParameters, false); sessionParameters[SessionParameter.BindingSpiClass] = BindingSpiBrowser; if (authenticationProvider == null) { if (!sessionParameters.ContainsKey(SessionParameter.AuthenticationProviderClass)) { sessionParameters[SessionParameter.AuthenticationProviderClass] = StandardAuthenticationProviderClass; } } if (!sessionParameters.ContainsKey(SessionParameter.HttpInvokerClass)) { sessionParameters[SessionParameter.HttpInvokerClass] = DefaultHttpInvokerClass; } AddDefaultParameters(sessionParameters); if (!sessionParameters.ContainsKey(SessionParameter.BrowserSuccinct)) { sessionParameters.Add(SessionParameter.BrowserSuccinct, "true"); } Check(sessionParameters, SessionParameter.BrowserUrl); return new CmisBinding(sessionParameters, authenticationProvider); } /// /// Creates an AtomPub binding object. /// /// the session parameters /// an authentication provider instance or null to use the default implementation /// a low-level binding object public ICmisBinding CreateCmisAtomPubBinding(IDictionary sessionParameters, IAuthenticationProvider authenticationProvider) { CheckSessionParameters(sessionParameters, false); sessionParameters[SessionParameter.BindingSpiClass] = BindingSpiAtomPub; if (authenticationProvider == null) { if (!sessionParameters.ContainsKey(SessionParameter.AuthenticationProviderClass)) { sessionParameters[SessionParameter.AuthenticationProviderClass] = StandardAuthenticationProviderClass; } } if (!sessionParameters.ContainsKey(SessionParameter.HttpInvokerClass)) { sessionParameters[SessionParameter.HttpInvokerClass] = DefaultHttpInvokerClass; } AddDefaultParameters(sessionParameters); Check(sessionParameters, SessionParameter.AtomPubUrl); return new CmisBinding(sessionParameters, authenticationProvider); } /// /// Creates a Web Services binding object. /// /// /// PortCMIS doesn't support the Web Services binding. It may be implemented in the future. /// /// the session parameters /// an authentication provider instance or null to use the default implementation /// a low-level binding object public ICmisBinding CreateCmisWebServicesBinding(IDictionary sessionParameters, IAuthenticationProvider authenticationProvider) { throw new ArgumentException("The Web Services binding is not supported!"); } // ---- internals ---- private void CheckSessionParameters(IDictionary sessionParameters, bool mustContainSpi) { // don't accept null if (sessionParameters == null) { throw new ArgumentNullException("sessionParameters"); } // check binding entry if (mustContainSpi) { string spiClass; if (sessionParameters.TryGetValue(SessionParameter.BindingSpiClass, out spiClass)) { throw new ArgumentException("SPI class entry (" + SessionParameter.BindingSpiClass + ") is missing!"); } if ((spiClass == null) || (spiClass.Trim().Length == 0)) { throw new ArgumentException("SPI class entry (" + SessionParameter.BindingSpiClass + ") is invalid!"); } } } private void Check(IDictionary sessionParameters, String parameter) { if (!sessionParameters.ContainsKey(parameter)) { throw new ArgumentException("Parameter '" + parameter + "' is missing!"); } } private void AddDefaultParameters(IDictionary sessionParameters) { foreach (string key in defaults.Keys) { if (!sessionParameters.ContainsKey(key)) { sessionParameters[key] = defaults[key]; } } } private IDictionary CreateNewDefaultParameters() { IDictionary result = new Dictionary(); result[SessionParameter.CacheSizeRepositories] = SessionParameterDefaults.CacheSizeRepositories.ToString(); result[SessionParameter.CacheSizeTypes] = SessionParameterDefaults.CacheSizeTypes.ToString(); result[SessionParameter.CacheSizeLinks] = SessionParameterDefaults.CacheSizeLinks.ToString(); return result; } } }