/*
* 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;
}
}
}