Разграничение доступа из кода в WCF - Реализация класса AppDomainHost

ОГЛАВЛЕНИЕ

Реализация класса AppDomainHost

Реализация класса выполняется в два приема: создание новой области приложений и внедрение экземпляра размещения службы, затем последующее выполнение размещения службы (и экземпляра службы) в условиях частичного доверия. Для создания экземпляра размещения службы и активирования его в отдельной области приложения я написал класс ServiceHostActivator, показанный на рис. 7.

Рис. 7 Класс ServiceHostActivator

class ServiceHostActivator : MarshalByRefObject {
  ServiceHost m_Host;

  public void CreateHost(Type serviceType,Uri[] baseAddresses) {
   m_Host = new ServiceHost(serviceType,baseAddresses);
  }
  public void Open() {
   m_Host.Open();
  }  
  public void Close() {
   m_Host.Close();
  }
  public void Abort() {
   m_Host.Abort();
  }

  //Rest of the implementation
}

Класс ServiceHostActivator представляет собой простую обертку стандартного экземпляра класса ServiceHost, предоставляемого платформой WCF. ServiceHostActivator порождается от MarshalByRefObject таким образом, чтобы класс AppDomainHost мог вызывать его через границу области приложений. В методе CreateHost инкапсулировано конструирование нового экземпляра ServiceHost. Остальные методы класса Service­HostActivator просто пересылают удаленные вызовы базовому экземпляру размещения.
AppDomainHost предлагает несколько перегружаемых конструкторов. Эти конструкторы могут вызывать друг друга (см. рис. 8), даже создавая по пути новую область приложений, и в конечном итоге конструирование завершается использованием защищенного конструктора, принимающего тип службы, экземпляр новой области приложений, полномочия для новой области и базовые адреса.

Рис. 8. Реализация класса AppDomainHost

public class AppDomainHost : IDisposable {
  ServiceHostActivator m_ServiceHostActivator;

  public AppDomainHost(Type serviceType,
   params Uri[] baseAddresses) :  
   this(serviceType,"AppDomain Host for "+
   serviceType+" "+Guid.NewGuid(),
   baseAddresses) {
  }

  public AppDomainHost(Type serviceType,
   string appDomainName,
   params Uri[] baseAddresses) : this(serviceType,
   new PermissionSet(PermissionState.Unrestricted),
   appDomainName,baseAddresses) {
  }

  public AppDomainHost(Type serviceType,
  PermissionSet permissions,
  string appDomainName,
  params Uri[] baseAddresses) :
  this(serviceType,AppDomain.CreateDomain(appDomainName),
  permissions,baseAddresses) {
  }

  //More constructors

  protected AppDomainHost(Type serviceType,
   AppDomain appDomain,
   PermissionSet permissions,Uri[] baseAddresses) {

   string assemblyName = Assembly.GetAssembly(
    typeof(ServiceHostActivator)).FullName;
   m_ServiceHostActivator = appDomain.CreateInstanceAndUnwrap(
    assemblyName,typeof(ServiceHostActivator).ToString()) as
    ServiceHostActivator;

   CodeAccessSecurityHelper.SetPermissionsSet(appDomain,permissions);

   m_ServiceHostActivator.CreateHost(serviceType,baseAddresses);
  }

  public void Open() {
   m_ServiceHostActivator.Open();
  }
  public void Close() {
   m_ServiceHostActivator.Close();
  }
  public void Abort() {
   m_ServiceHostActivator.Abort();
  }
  void IDisposable.Dispose() {
   Close();
  }
}

Защищенный конструктор класса AppDomainHost использует технологию удаленного вызова .NET для внедрения в новую область приложений экземпляра класса ServiceHostActivator, завершая предоставлением ему удаленного прокси, хранящегося в члене m_ServiceHostActivator.

По умолчанию новая область приложений создается с полным доверием. Класс AppDomainHost использует метод SetPermissionsSet моего класса CodeAccessSecurityHelper для установки в новой области приложений новой политики CAS. Эта политика разрешает только предлагаемые полномочия и отказывает в остальных.

public static class CodeAccessSecurityHelper {
  public static void SetPermissionsSet(
   AppDomain appDomain,
   PermissionSet permissions) {

   PolicyLevel policy = PolicyLevel.CreateAppDomainLevel();
   policy.RootCodeGroup.PolicyStatement =
    new PolicyStatement(permissions);
   appDomain.SetAppDomainPolicy(policy);
  }
  //More members
}

Это так же просто, как создание новой политики безопасности на уровне области приложений и вызов метода SetAppDomainPolicy класса AppDomain.

public sealed class AppDomain :
  MarshalByRefObject,... {

  public void SetAppDomainPolicy(
   PolicyLevel domainPolicy);
  //More members
}

Между прочим, подобный прием используется в технологии ClickOnce для принудительного частично доверенного выполнения приложений, развернутых с применением ClickOnce.

При вызове других методов класса AppDomainHost, таких как Open или Close, используется прокси класса ServiceHostActivator с целью вызова другой области приложений и обеспечения открытия или закрытия им его экземпляра размещения. Поскольку экземпляр службы будет выполняться в области приложений, которой случилось открыть его размещение, служба также будет выполняться согласно политике безопасности этой области приложений.