diff --git a/Elfo.Wardein.Abstractions/Configuration/Models/WatcherModels/WebWatcherConfigurationModel.cs b/Elfo.Wardein.Abstractions/Configuration/Models/WatcherModels/WebWatcherConfigurationModel.cs
index ad6f832..d0e2cc8 100644
--- a/Elfo.Wardein.Abstractions/Configuration/Models/WatcherModels/WebWatcherConfigurationModel.cs
+++ b/Elfo.Wardein.Abstractions/Configuration/Models/WatcherModels/WebWatcherConfigurationModel.cs
@@ -9,10 +9,10 @@ public class WebWatcherConfigurationModel : IAmConfigurationModelWithResolution
{
[JsonProperty(PropertyName = "associatedIISPool")]
public string AssociatedIISPool { get; set; }
-
+
[JsonProperty(PropertyName = "url")]
public Uri Url { get; set; }
-
+
[JsonProperty(PropertyName = "urlAlias")]
public string UrlAlias { get; set; }
@@ -43,5 +43,22 @@ public class WebWatcherConfigurationModel : IAmConfigurationModelWithResolution
public string RestoredMessage { get; set; }
public int MaxRetryCount { get; set; } = 2;
public int SendReminderEmailAfterRetryCount { get; set; } = 120;
+ public enum HttpCallApiMethod
+ {
+ Get = 1,
+ Post = 2
+ }
+
+ public HttpCallApiMethod Method { get; }
+
+ ///
+ /// Request Body that may be required for POST request.
+ ///
+ public string Body { get; } = null;
+
+ ///
+ /// Request headers.
+ ///
+ public IDictionary Headers { get; } = null;
}
}
diff --git a/Elfo.Wardein.Abstractions/WebWatcher/IAmUrlResponseManager.cs b/Elfo.Wardein.Abstractions/WebWatcher/IAmUrlResponseManager.cs
index 068baff..ad95356 100644
--- a/Elfo.Wardein.Abstractions/WebWatcher/IAmUrlResponseManager.cs
+++ b/Elfo.Wardein.Abstractions/WebWatcher/IAmUrlResponseManager.cs
@@ -1,11 +1,12 @@
-using System;
+using Elfo.Wardein.Abstractions.Configuration.Models;
using System.Threading.Tasks;
+using static Elfo.Wardein.Abstractions.Configuration.Models.WebWatcherConfigurationModel;
namespace Elfo.Wardein.Abstractions.WebWatcher
{
public interface IAmUrlResponseManager
{
- Task IsHealthy(bool assertWithStatusCode, string assertWithRegex, Uri url);
+ Task IsHealthy(WebWatcherConfigurationModel configuration, HttpCallApiMethod method);
Task RestartPool(string poolName);
}
}
diff --git a/Elfo.Wardein.Core.Tests/WebWatcherCallWithApiClient.cs b/Elfo.Wardein.Core.Tests/WebWatcherCallWithApiClient.cs
new file mode 100644
index 0000000..0eacc27
--- /dev/null
+++ b/Elfo.Wardein.Core.Tests/WebWatcherCallWithApiClient.cs
@@ -0,0 +1,105 @@
+using Elfo.Wardein.Abstractions.Configuration.Models;
+using Microsoft.Extensions.Configuration;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Microsoft.Win32.SafeHandles;
+using System;
+using System.Net.Http;
+using System.Net.Http.Headers;
+using System.Runtime.InteropServices;
+using System.Security.Permissions;
+using System.Security.Principal;
+using System.Threading.Tasks;
+
+namespace Elfo.Wardein.Core.Tests
+{
+
+ [TestClass]
+ public class WebWatcherCallWithApiClient
+ {
+ private WebWatcherConfigurationModel configuration;
+ private string userNameToImpersonate;
+ private string domainToImpersonate;
+ private string userPasswordToImpersonate;
+
+ [TestInitialize]
+ public void Initialize()
+ {
+ var configuration = new ConfigurationBuilder()
+ .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
+ .Build();
+
+ userNameToImpersonate = configuration["Impersonate:userNameToImpersonate"];
+ domainToImpersonate = configuration["Impersonate:userDomainToImpersonate"];
+ userPasswordToImpersonate = configuration["Impersonate:userPasswordToImpersonate"];
+ }
+
+ async Task IsSuccessStatusCode(HttpClient client)
+ {
+ var response = await client.GetAsync(configuration.Url.AbsoluteUri);
+ return response.IsSuccessStatusCode;
+ }
+
+ [TestMethod]
+ [TestCategory("ManualTest")]
+ public async Task IsHealthy()
+ {
+ var client = new HttpClient(new HttpClientHandler() { UseDefaultCredentials = true, PreAuthenticate = true, AllowAutoRedirect = true });
+ client.BaseAddress = new Uri(configuration.Url.AbsoluteUri);
+ client.DefaultRequestHeaders.Accept.Clear();
+ client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
+ var response = false;
+ try
+ {
+ ImpersonateUser iu = new ImpersonateUser();
+ iu.RunImpersonated(domainToImpersonate, userNameToImpersonate, userPasswordToImpersonate,
+ () => IsSuccessStatusCode(client).Wait());
+ response = await IsSuccessStatusCode(client);
+ }
+ catch(Exception ex) {
+ Console.WriteLine($"There is an error: {ex}");
+ }
+
+ Assert.IsTrue(response);
+ }
+ }
+
+ [TestClass]
+ public class ImpersonateUser
+ {
+ [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
+ public static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword,
+ int dwLogonType, int dwLogonProvider, out SafeAccessTokenHandle phToken);
+
+ // Test harness.
+ // If you incorporate this code into a DLL, be sure to demand FullTrust.
+ [TestMethod]
+ [TestCategory("ManualTest")]
+ [PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
+ public void RunImpersonated(string domainName, string userName, string password, Action action)
+ {
+ try
+ {
+ const int LOGON32_PROVIDER_DEFAULT = 0;
+ //This parameter causes LogonUser to create a primary token.
+ const int LOGON32_LOGON_INTERACTIVE = 2;
+
+ // Call LogonUser to obtain a handle to an access token.
+ SafeAccessTokenHandle safeAccessTokenHandle;
+ bool returnValue = LogonUser(userName, domainName, password,
+ LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,
+ out safeAccessTokenHandle);
+
+ if (false == returnValue)
+ {
+ int ret = Marshal.GetLastWin32Error();
+ throw new System.ComponentModel.Win32Exception(ret);
+ }
+ WindowsIdentity.RunImpersonated(safeAccessTokenHandle, action);
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine("Exception occurred. " + ex.Message);
+ }
+ }
+ }
+}
diff --git a/Elfo.Wardein.Core/Elfo.Wardein.Core.csproj b/Elfo.Wardein.Core/Elfo.Wardein.Core.csproj
index 7fd153a..da1dadc 100644
--- a/Elfo.Wardein.Core/Elfo.Wardein.Core.csproj
+++ b/Elfo.Wardein.Core/Elfo.Wardein.Core.csproj
@@ -24,6 +24,8 @@
+
+
diff --git a/Elfo.Wardein.Core/ServiceManager/HttpClientUrlResponseManager.cs b/Elfo.Wardein.Core/ServiceManager/HttpClientUrlResponseManager.cs
index a8eb1b4..a141aab 100644
--- a/Elfo.Wardein.Core/ServiceManager/HttpClientUrlResponseManager.cs
+++ b/Elfo.Wardein.Core/ServiceManager/HttpClientUrlResponseManager.cs
@@ -1,50 +1,77 @@
-using Elfo.Wardein.Abstractions.WebWatcher;
-using Elfo.Wardein.Core.ServiceManager;
+using Elfo.Wardein.Abstractions.Configuration.Models;
+using Elfo.Wardein.Abstractions.WebWatcher;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Win32.SafeHandles;
+using NLog;
using System;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
+using System.Runtime.InteropServices;
+using System.Security.Permissions;
+using System.Security.Principal;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
+using static Elfo.Wardein.Abstractions.Configuration.Models.WebWatcherConfigurationModel;
namespace Elfo.Wardein.Core.ServiceManager
{
public class HttpClientUrlResponseManager : IAmUrlResponseManager
{
+ private string userNameToImpersonate;
+ private string domainToImpersonate;
+ private string userPasswordToImpersonate;
- public async Task IsHealthy(bool assertWithStatusCode, string assertWithRegex, Uri url)
+ protected static ILogger log = LogManager.GetCurrentClassLogger();
+
+ public HttpClientUrlResponseManager()
{
- using (var handler = new HttpClientHandler())
- using (var client = new HttpClient(handler))
- {
- // TODO support authentication
- client.BaseAddress = url;
- client.DefaultRequestHeaders.Accept.Clear();
- client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
+ var configuration = new ConfigurationBuilder()
+ .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
+ .Build();
+
+ userNameToImpersonate = configuration["Impersonate:userNameToImpersonate"];
+ domainToImpersonate = configuration["Impersonate:userDomainToImpersonate"];
+ userPasswordToImpersonate = configuration["Impersonate:userPasswordToImpersonate"];
+ }
- var response = await client.GetAsync(url);
+
- if (!assertWithStatusCode)
+ public async Task IsHealthy(WebWatcherConfigurationModel configuration, HttpCallApiMethod method)
+ {
+ HttpResponseMessage response = null;
+ var apiClient = InitializeApiClient(configuration);
+ ImpersonateUser iu = new ImpersonateUser();
+ try
+ {
+ iu.RunImpersonated(domainToImpersonate, userNameToImpersonate, userPasswordToImpersonate,
+ async () => response = await apiClient.GetAsync(configuration.Url.AbsoluteUri));
+ }
+ catch (UnauthorizedAccessException ex)
+ {
+ log.Error($"Exception got while waiting response from {configuration.Url.AbsoluteUri} - {ex}");
+ throw;
+ }
+ if (!configuration.AssertWithStatusCode)
+ {
+ if (!string.IsNullOrWhiteSpace(configuration.AssertWithRegex))
{
- if (!string.IsNullOrWhiteSpace(assertWithRegex))
- {
- return await CheckIsMatch(assertWithRegex, response);
- }
- else
- {
- return await Task.FromResult(true);
- }
+ return await CheckIsMatch(configuration.AssertWithRegex, response);
}
else
{
- if (response.StatusCode != HttpStatusCode.OK)
- {
- return await Task.FromResult(false);
- }
- else
- {
- return await CheckIsMatch(assertWithRegex, response);
- }
+ return await Task.FromResult(true);
+ }
+ }
+ else
+ {
+ if (response.StatusCode != HttpStatusCode.OK)
+ {
+ return await Task.FromResult(false);
+ }
+ else
+ {
+ return await CheckIsMatch(configuration.AssertWithRegex, response);
}
}
}
@@ -70,33 +97,57 @@ private async Task CheckIsMatch(string assertionRegex, HttpResponseMessage
}
}
- public Task RestartPool(string poolName)
+ public async Task RestartPool(string poolName)
{
- new IISPoolManager(poolName).Restart();
- return Task.CompletedTask;
+ await new IISPoolManager(poolName).Restart();
}
- //private List AuthCredentials(string username, string password)
- //{
- // List result = null;
+ HttpClient InitializeApiClient(WebWatcherConfigurationModel configuration)
+ {
+ var client = new HttpClient(new HttpClientHandler() { UseDefaultCredentials = true, PreAuthenticate = true });
+ client.BaseAddress = new Uri(configuration.Url.AbsoluteUri);
+ client.DefaultRequestHeaders.Accept.Clear();
+ client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
+
+ return client;
+ }
+
+ public class ImpersonateUser
+ {
+ [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
+ public static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword,
+ int dwLogonType, int dwLogonProvider, out SafeAccessTokenHandle phToken);
+
+ // Test harness.
+ // If you incorporate this code into a DLL, be sure to demand FullTrust.
+
+ [PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
+ public void RunImpersonated(string domainName, string userName, string password, Action action)
+ {
+ try
+ {
+ const int LOGON32_PROVIDER_DEFAULT = 0;
+ //This parameter causes LogonUser to create a primary token.
+ const int LOGON32_LOGON_INTERACTIVE = 2;
- // CredentialCache.DefaultNetworkCredentials.UserName = username;
- // CredentialCache.DefaultNetworkCredentials.Password = password;
+ // Call LogonUser to obtain a handle to an access token.
+ SafeAccessTokenHandle safeAccessTokenHandle;
+ bool returnValue = LogonUser(userName, domainName, password,
+ LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,
+ out safeAccessTokenHandle);
- // using (var authtHandler = new HttpClientHandler { Credentials = CredentialCache.DefaultNetworkCredentials })
- // {
- // using (var httpClient = new HttpClient(authtHandler))
- // {
- // httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("Keep-Alive"));
- // HttpResponseMessage message = httpClient.GetAsync("").Result;
- // if (message.IsSuccessStatusCode)
- // {
- // var inter = message.Content.ReadAsStringAsync();
- // result = JsonConvert.DeserializeObject>(inter.Result);
- // }
- // }
- // }
- // return result;
- //}
+ if (false == returnValue)
+ {
+ int ret = Marshal.GetLastWin32Error();
+ throw new System.ComponentModel.Win32Exception(ret);
+ }
+ WindowsIdentity.RunImpersonated(safeAccessTokenHandle, action);
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine("Exception occurred. " + ex.Message);
+ }
+ }
+ }
}
}
diff --git a/Elfo.Wardein.Watchers/WebWatcher/WebWatcher.cs b/Elfo.Wardein.Watchers/WebWatcher/WebWatcher.cs
index 2053034..ad9596b 100644
--- a/Elfo.Wardein.Watchers/WebWatcher/WebWatcher.cs
+++ b/Elfo.Wardein.Watchers/WebWatcher/WebWatcher.cs
@@ -1,9 +1,7 @@
using Elfo.Wardein.Abstractions.Watchers;
using Elfo.Wardein.Abstractions.WebWatcher;
-using Elfo.Wardein.Core.ServiceManager;
using Elfo.Wardein.Core;
using Elfo.Wardein.Core.Helpers;
-using Elfo.Wardein.Core.NotificationService;
using NLog;
using NLog.Fluent;
using System;
@@ -63,7 +61,7 @@ internal virtual async Task RunCheck()
log.Info($"Starting check on {GetLoggingDisplayName}");
var notificationService = ServicesContainer.NotificationService(Config.NotificationType);
- var isHealthy = await urlResponseManager.IsHealthy(Config.AssertWithStatusCode, Config.AssertWithRegex, Config.Url);
+ var isHealthy = await urlResponseManager.IsHealthy(Config, Config.Method);
var currentStatus = await watcherPersistenceService.UpsertCurrentStatus
(
watcherConfigurationId: Config.WatcherConfigurationId,