C# WPF, выполняющий NativeActivity, выполняющий олицетворение - PullRequest
0 голосов
/ 05 марта 2020

Я пытаюсь выполнить действия WPF в олицетворенной области. Я использую класс, полученный из NativeActivity, и добавляю в него действия. Я могу ввести олицетворение и выполнить код под олицетворением, но я предполагаю, что сам контекст NativeActivity не находится в контексте олицетворения.

    class Program {
            static void Main(string[] args) {
                 //this part of code is used for testing
                Console.WriteLine("starting");

                //arguments for activity execution
                Dictionary<string, object> arguments = new Dictionary<string, object> {
                    {"Domain", "domain" },
                    {"UserName","account" },
                    {"Password","password" },                
                };

                WindowsImpersonation.ExecuteUnderImpersonation act = new WindowsImpersonation.ExecuteUnderImpersonation();
                act.LogonAuthType = WindowsImpersonation.LogonType.LOGON32_LOGON_NEW_CREDENTIALS;

                //adding testing activity
                act.Activities.Add( new TestingActivity() );

                //running activity
                WorkflowInvoker.Invoke(act, arguments );


                Console.ReadLine();

            }
    }

Тестирование - это просто запись в текстовый файл. Я знаю, что учетная запись, которую я олицетворяю, не имеет доступа к этому общему диску

namespace WindowsImpersonation {
    public class TestingActivity: CodeActivity {

        protected override void Execute (CodeActivityContext context) {
            Console.WriteLine("inner activity");

            try {
                File.AppendAllText( @"\\shareddrive\test.txt", "\n" + "test: " + DateTime.Now.ToString( "HH:mm:ss" ) );
                Console.WriteLine("Written into file from inner activity");
            }
            catch ( Exception exception ) {
                Console.WriteLine( "Error in inner activity when appending hardcoded: " + exception.Message.ToString() );
            }

        }

    }
}

Для самой деятельности. Цель состоит в том, чтобы выполнить TestActivity в области олицетворения. Попытка записи в файл непосредственно в RunImpersonated встречается с ошибкой, поскольку он не имеет к ней доступа. Но контекст NativeActivity выполняется под моим пользователем (не олицетворенным пользователем), поэтому ему удается получить доступ к файлу и выполнить запись в него.

namespace WindowsImpersonation
{
    [Designer( "System.Activities.Core.Presentation.SequenceDesigner, System.Activities.Core.Presentation" )]
    public partial class ExecuteUnderImpersonation : NativeActivity {
        internal static string ParentContainerPropertyTag => "ExecuteUnderImpersonation";

        private Sequence innerSequence = new Sequence();        

        [Browsable( false )]
        public Collection<Activity> Activities {
            get {
                return innerSequence.Activities;
            }
        }

        [Browsable( false )]
        public Collection<Variable> Variables {
            get {
                return innerSequence.Variables;
            }
        }

        protected override void CacheMetadata( NativeActivityMetadata metadata ) {            
            metadata.AddImplementationChild( innerSequence );
            base.CacheMetadata( metadata );
        }        

        protected override void Execute( NativeActivityContext context ) {
            //this properties are declared in different file (partial class), not pasting them here
            string userName = UserName.Get( context );
            string password = Password.Get( context );
            string domain = Domain.Get( context );
            LogonType logonType = LogonAuthType;



            IntPtr userToken = GetUserTokenHandle( domain, userName, password, (int)logonType );


            Console.WriteLine("executing shit");
            WindowsIdentity.RunImpersonated( new Microsoft.Win32.SafeHandles.SafeAccessTokenHandle( userToken ), () => {

                //this part gets executed as impersonated and it throws error as account does not have access there.
                try {
                    File.AppendAllText( @"shareddrive\test.txt", "\n" + "test: " + DateTime.Now.ToString( "HH:mm:ss" ) );
                }
                catch ( Exception exception ) {
                    Console.WriteLine( "error when appending hardcoded: " + exception.Message.ToString() );
                }


                //this does not execte as impersonated. Test activity is able to access the file
                // I assume it is because native activity context itself is not under impersonation
                context.ScheduleActivity( innerSequence, OnCompleted, OnFaulted );
            } );


            #region test direct
            //WindowsIdentity newId = new WindowsIdentity( new Microsoft.Win32.SafeHandles.SafeAccessTokenHandle( userToken ).DangerousGetHandle() );
            //WindowsImpersonationContext impersonation = null;
            //impersonation = newId.Impersonate();


            //try {
            //    File.AppendAllText( @"shareddrive\test.txt", "\n" + "test: " + DateTime.Now.ToString( "HH:mm:ss" ) );
            //}
            //catch ( Exception exception ) {
            //    Console.WriteLine( "error when appending hardcoded: " + exception.Message.ToString() );
            //}



            //context.ScheduleActivity( innerSequence, OnCompleted, OnFaulted );

            //impersonation.Dispose();

            #endregion direct end


            #region olderMethod
            //using ( WindowsImpersonationContext contextImpersonation = WindowsIdentity.Impersonate( userToken ) ) {
            //    try {
            //        File.AppendAllText( @"\\shareddrive\test.txt", "\n" + "test: " + DateTime.Now.ToString( "HH:mm:ss" ) );
            //    }
            //    catch ( Exception exception ) {
            //        Console.WriteLine( "error when appending hardcoded: " + exception.Message.ToString() );
            //    }


            //    context.ScheduleActivity( innerSequence, OnCompleted, OnFaulted );
            //}
            #endregion oldermethod

        }

        private void OnFaulted( NativeActivityFaultContext faultContext, Exception propagatedException, ActivityInstance propagatedFrom ) {
            Console.WriteLine( "On Faulted executed" );
        }

        private void OnCompleted( NativeActivityContext context, ActivityInstance completedInstance ) {
            Console.WriteLine( "OnCompleted exected" );
        }

    }
}

Маркер пользователя создается с помощью функции. Может публиковать весь код, если требуется

[DllImport( "advapi32.dll", SetLastError = true )]
        public static extern bool LogonUser(
            string lpszUsername,
            string lpszDomain,
            string lpszPassword,
            int dwLogonType,
            int dwLogonProvider,
            out IntPtr phToken
        );

Как принудительно настроить контекст NativeActivity в контексте олицетворения?

Спасибо

РЕДАКТИРОВАТЬ1: Запуск дополнительных тестов Я заметил, что запланировано действие и фактически выполняется за пределами области олицетворения

logs:
exiting impersonation scope
exited
inner activity running

Таким образом, мы можем переосмыслить вопрос, как заставить context.ScheduleActivity( innerSequence, OnCompleted, OnFaulted ) выполнить и завершить sh до выхода из области олицетворения.

Я почти был в состоянии заставить его работать с небольшими изменениями

protected override void CacheMetadata( NativeActivityMetadata metadata ) {
    //commenting out this part
    //metadata.AddImplementationChild( innerSequence );
    base.CacheMetadata( metadata );
} 

И затем с помощью WorkflowInvoker WorkflowInvoker.Invoke( innerSequence ); Это позволяет добиться того, что я пытаюсь сделать с записью в файл, но в тот момент, когда я начинаю использовать более сложные действия, включающие БД соединение, я сталкиваюсь с другой ошибкой

System.Activities.InvalidWorkflowException: The following errors were encountered while processing the workflow tree:
'VisualBasicValue<Boolean>': Compiler error(s) encountered processing expression "cnn isnot nothing".
Type 'DatabaseConnection' is not defined.

РЕДАКТИРОВАТЬ 2: я был в состоянии исследовать это дальше. Реализация

    internal class ImpersonationScope : IExecutionProperty  {
        public const string Name = "ImpersonationScope";

        private WindowsImpersonationContext impersonation = null;
        IntPtr userToken = IntPtr.Zero;

        public ImpersonationScope(
            string domain, string userName, string password, int logonType ) {

            userToken = GetUserTokenHandle( domain, userName, password, logonType );

        }

        void IExecutionProperty.SetupWorkflowThread() {

            WindowsIdentity newId = new WindowsIdentity( new Microsoft.Win32.SafeHandles.SafeAccessTokenHandle( userToken ).DangerousGetHandle() );
            impersonation = newId.Impersonate();

        }

        void IExecutionProperty.CleanupWorkflowThread() {                
            impersonation.Dispose();
        }
    }

Сохранение

protected override void CacheMetadata( NativeActivityMetadata metadata ) {            
    metadata.AddImplementationChild( innerSequence );
    base.CacheMetadata( metadata );
} 

Затем я могу использовать этот код

ImpersonationScope impersonationScope = new ImpersonationScope(domain,userName,password,(int)logonType);

            context.Properties.Add( ImpersonationScope.Name, impersonationScope );
            context.ScheduleActivity( innerSequence, OnCompleted, OnFaulted );

Это выполняет действие в рамках олицетворенной области. Тем не менее, я сталкиваюсь с ошибкой, что при использовании новых сетевых учетных данных типа входа в систему он не распределяет олицетворение полностью, и я застреваю с олицетворенными сетевыми учетными данными. Причины проблемы.

РЕДАКТИРОВАТЬ 3: Надеюсь, что окончательное редактирование. Кажется, все работает сейчас. Последнее изменение - добавление .Impersonate (IntPtr.Zero), при котором Win32 API возвращается к себе

    void IExecutionProperty.CleanupWorkflowThread() {                
        impersonation.Dispose();
        WindowsIdentity.Impersonate( IntPtr.Zero );
    }
...