using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Permissions;
using System.Security.Principal;
using System.Text;
using System.Threading.Tasks;

namespace Valg.EVA.Skanning.Services.Security
{
	[PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
	public sealed class Impersonation : IDisposable
	{
		private readonly SafeTokenHandle _handle;
		private readonly WindowsImpersonationContext _context;

		
		
		
		
		
		
		
		
		
		
		public static Impersonation LogonUser(string domain, string username, string password, LogonType logonType)
		{
			return new Impersonation(domain, username, password, logonType);
		}

		
		
		
		
		
		
		
		
		
		
		public static Impersonation LogonUser(string domain, string username, SecureString password, LogonType logonType)
		{
			return new Impersonation(domain, username, password, logonType);
		}

		private Impersonation(string domain, string username, SecureString password, LogonType logonType)
		{
			IntPtr token;
			IntPtr passPtr = Marshal.SecureStringToGlobalAllocUnicode(password);

			bool success;
			try
			{
				success = NativeMethods.LogonUser(username, domain, passPtr, (int)logonType, 0, out token);
			}
			finally
			{
				Marshal.ZeroFreeGlobalAllocUnicode(passPtr);
			}

			CompleteImpersonation(success, token, out _handle, out _context);
		}

		private Impersonation(string domain, string username, string password, LogonType logonType)
		{
			IntPtr token;
			bool success = NativeMethods.LogonUser(username, domain, password, (int)logonType, 0, out token);
			CompleteImpersonation(success, token, out _handle, out _context);
		}

		private void CompleteImpersonation(bool success, IntPtr token, out SafeTokenHandle handle, out WindowsImpersonationContext context)
		{
			if (!success)
			{
				var errorCode = Marshal.GetLastWin32Error();

				if (token != IntPtr.Zero)
				{
					NativeMethods.CloseHandle(token);
				}

				throw new ImpersonationException(new Win32Exception(errorCode));
			}

			handle = new SafeTokenHandle(token);
			context = WindowsIdentity.Impersonate(_handle.DangerousGetHandle());
		}

		
		
		
		public void Dispose()
		{
			_context.Dispose();
			_handle.Dispose();
		}
	}
}

