//------------------------------------------------------------------------------------------------------
//  
//   Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//----------------------------------------------------------------------------------------------------
namespace System.ServiceModel.Security
{
    using System.Runtime;
    using System.Security;
    using System.Security.Principal;
    using System.ServiceModel;
    using System.ServiceModel.Channels;
    using System.ServiceModel.Dispatcher;
    using System.ServiceModel.Diagnostics;
    /// 
    /// The helper class to enable impersonation while serializing the body of the reply message.
    /// 
    public class ImpersonateOnSerializingReplyMessageProperty : IMessageProperty
    {
        const string PropertyName = "ImpersonateOnSerializingReplyMessageProperty";
        MessageRpc rpc;
        internal ImpersonateOnSerializingReplyMessageProperty(ref MessageRpc rpc)
        {
           this.rpc = rpc;
        }
        /// 
        /// Gets the name of the message property.
        /// 
        public static string Name
        {
            get { return PropertyName; }
        }
        /// 
        /// Gets the ImpersonateOnSerializingReplyMessageProperty property from a message.
        /// 
        /// The message to extract the property from.
        /// An output paramter to hold the ImpersonateOnSerializingReplyMessageProperty property.
        /// True if the ImpersonateOnSerializingReplyMessageProperty property was found.
        public static bool TryGet(Message message, out ImpersonateOnSerializingReplyMessageProperty property)
        {
            if (message == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("message");
            }
            return TryGet(message.Properties, out property);
        }
        /// 
        /// Gets the ImpersonateOnSerializingReplyMessageProperty property from MessageProperties.
        /// 
        /// The MessagePropeties object.
        /// An output paramter to hold the ImpersonateOnSerializingReplyMessageProperty property.
        /// True if the ImpersonateOnSerializingReplyMessageProperty property was found.
        public static bool TryGet(MessageProperties properties, out ImpersonateOnSerializingReplyMessageProperty property)
        {
            if (properties == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("properties");
            }
            object value = null;
            if (properties.TryGetValue(PropertyName, out value))
            {
                property = value as ImpersonateOnSerializingReplyMessageProperty;
            }
            else
            {
                property = null;
            }
            return property != null;
        }
        
        /// 
        /// Creates a copy of the message property.
        /// 
        /// Returns a copy of the message property.
        public IMessageProperty CreateCopy()
        {
            ImpersonateOnSerializingReplyMessageProperty result = new ImpersonateOnSerializingReplyMessageProperty(ref this.rpc);
            return result;
        }
              
        /// 
        /// Starts Impersonating with the caller's context if impersonation is enabled on the service and sets the appropriate principal on the thread as per the service configuration.
        /// 
        /// The impersonated context.
        /// The original principal on the thread before invoking this method.
        /// The value determines if the principal was set on the thread by the method.
        /// Returns false if operation context was not available to impersonate.
        [Fx.Tag.SecurityNote(Critical = "Calls SecurityCritical method StartImpersonation.", Safe = "Manages the result of impersonation and properly Disposes it.")]
        [SecuritySafeCritical]
        public void StartImpersonation(out IDisposable impersonationContext, out IPrincipal originalPrincipal, out bool isThreadPrincipalSet)
        {
            impersonationContext = null;
            originalPrincipal = null;
            isThreadPrincipalSet = false;
            if (OperationContext.Current != null)
            {
                EndpointDispatcher endpointDispatcher = OperationContext.Current.EndpointDispatcher;
                if (endpointDispatcher != null)
                {
                    DispatchRuntime dispatchRuntime = endpointDispatcher.DispatchRuntime;
                    ImmutableDispatchRuntime runtime = dispatchRuntime.GetRuntime();
                    if (runtime != null && runtime.SecurityImpersonation != null)
                    {
                        runtime.SecurityImpersonation.StartImpersonation(ref this.rpc, out impersonationContext, out originalPrincipal, out isThreadPrincipalSet);
                    }
                }
            }
        }
        /// 
        /// Reverts impersonation and sets the original principal on the thread.
        /// 
        /// The impersonation context to revert.
        /// The original principal to set on the thread.
        /// The value determines if the thread principal was set during impersonation.
        /// Returns false if operation context was not available to revert the impersonation.
        [Fx.Tag.SecurityNote(Critical = "Calls SecurityCritical method StartImpersonation.", Safe = "Manages the result of impersonation and properly Disposes it.")]
        [SecuritySafeCritical]
        public void StopImpersonation(IDisposable impersonationContext, IPrincipal originalPrincipal, bool isThreadPrincipalSet)
        {
            if (OperationContext.Current != null)
            {
                EndpointDispatcher endpointDispatcher = OperationContext.Current.EndpointDispatcher;
                if (endpointDispatcher != null)
                {
                    DispatchRuntime dispatchRuntime = endpointDispatcher.DispatchRuntime;
                    ImmutableDispatchRuntime runtime = dispatchRuntime.GetRuntime();
                    if (runtime != null && runtime.SecurityImpersonation != null)
                    {
                       runtime.SecurityImpersonation.StopImpersonation(ref this.rpc, impersonationContext, originalPrincipal, isThreadPrincipalSet);
                    }
                }
            }
        }
    }
}