Я пытаюсь выполнить действия 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 );
}